Upload (access)image (or video or any content) to S3 via pre-signed Url with NodeJs

Signed url is a url, which is generated by an authorised account. Anyone has the signed url can use it at any time and many times as they need as long as the url hasn’t expired.
Why should we use signed url?
Uploading heavy content from S3 via your own server consumes its resources and your front-end might need to wait longer for images being uploaded to AWS S3, you pass that job to AWS by using signed url.
Any drawbacks?
As the definition above “Anyone has the signed url can use it at any time and many times as they need as long as the url hasn’t expired.”. Anyone has the url can access your content is not what you want for sure. But we can use AWS S3’s policies and permission to minimise this soon.
Let’s hand on how to generate and use a signed url
- Create a S3 bucket
- Create a NodeJs server serving 2 apis
signed-url-upload
(for uploading image) andsigned-url-access
(for accessing image) - Using Postman to test apis and signed url
1. Create a S3 bucket
- If you don’t have AWS account yet. Follow this to create one
- Then create an S3 bucket by following those step.
- We also need AWS access key and secret key when generating signed url, following this to create one pair if you don’t have yet
Note: Keep block all checked to keep your bucket private.

You might need to setup CORs for your S3 bucket:

2. Create a NodeJs server
Create NodeJs express server using type script.
- create a folder (I use
signed-url-demo
) - setup simple express typescript server
- install aws-sdk:
yarn add aws-sdk
Create first signed-url-upload
endpoint
Code explain
- You can pass
fileName
,extension
andmediaType
the name and extension that you want for the content when it store in S3 and the type corresponding to that content, I used"demo-image.png”
as default thenmediaType
must be image. - Bucket is name of the bucket that we just created
tony-signed-url-demo
- Key is combination of
fileName
andextension
will be media name in S3demo-image.png
- Expires is the second that the signed url will be expired in
- ContentType is type of your media
image/png
, all available here - This demo calls AWS directly from local so we need to pass
accessKey
andsecretKey
from step 1, when create S3 instance too. - When calling
s3.getSignedUrlPromis...
we pass"putObject"
operation as first parameter because we are creating signed url to upload image. For accessing image the operation will be"getObject"
we will implement it below
Create first signed-url-access
endpoint
Code explain
accessKey
, secretKey
, Bucket
, Key
, Expires
same as signed-url-upload
endpoint, except:
- Don’t need
ContentType
, S3 only needKey
which is the name of the media stored in S3 to get media (image) - Operation passed into
s3.getSignedUrlPromis...
is"getObject"
for accessing
3. Using postman to test
- Get signed url for uploading image

- Using signed url to upload image

- Check S3 bucket to make sure the image is uploaded

- Get signed url for accessing image

- Using the signed url to access image

3. Security
As aforementioned, anyone has valid signed url (haven’t expired yet) can access your content, this rarely happen but still. We can do a little bit more to prevent your content is used in other websites by setting permission for your PRIVATE BUCKET
{
"Version": "2012-10-17",
"Id": "http referer policy example",
"Statement": [
{
"Sid": "Deny any domain except yourdomain access your content.",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::tony-signed-url-demo/*",
"Condition": {
"StringNotLike": {
"aws:Referer": [
"https://www.yourdomain.com/*",
"http://yourdomain.com/*"
]
}
}
}
]
}
This policy denies any domain even postman or directly accessing signed url from browser, it only allows your domain to access your content only.
Note: You might get access denied when trying to create this policy, you need to:
- Uncheck Block all public access
- Have
s3:putBucketPolicy
action permission for your user
After add S3 policy, remember to check Block all public access again to keep your bucket private
4. Resources
- You can find source code here
- There also is a great article from AWS demo and explain about signed url but it has some unnecessary complicated setup process.
Thanks for your reading.
Happy Coding 🧑💻!