S3 Presigned URLs: Secure and Temporary Access to Your Objects

Introduction
In the previous article of this series I showed you how to use ‘bucket policies’ to manage access to your S3 buckets. In this article, we’ll talk about how to proceed when we need to grant access to a file while still wanting to block public access to our S3 bucket. This is one of the advanced topics related to S3 buckets.
Context
Suppose an external service needs to access an object stored in an S3 bucket. For example, a service that has to read the same object every day to process the information it contains. With what we know so far, there are a few ways to achieve this. One is by allowing public access to the bucket and, through bucket policies, permitting access based on IP addresses and specific tags assigned to the object. However, this method has several drawbacks. First, even if we limit access through policies, having the bucket publicly accessible is not the best option from a security standpoint, especially if the data is private by nature. Precisely with what you’ll learn in this article, we’ll be able to solve this problem without relying on the bucket being public.
Presigned URL
A presigned URL is essentially a security token generated by an entity (IAM user or role) that already has permissions to perform that action. The URL contains the signature of the creator, and AWS only verifies if that entity still has permissions to execute the action and if the signature is still valid. This way, we can generate a presigned URL to retrieve a specific object from an S3 bucket without granting public access to the bucket.
Anatomy of a Presigned URL
This is an example of a presigned URL to retrieve a specific object from an S3 bucket:
https://compacompia-test.s3.us-east-1.amazonaws.com/avatar.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAQSOI4GGCSHG5NDCO%2F20260517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260517T120057Z&X-Amz-Expires=300&X-Amz-Security-Token=IQoJb3JpZ2luX2VjENz%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQC3Ww4rsAvnrog2KcQYLXnNbiUd%2B0JWbtkcBpqXTWUSxgIhAMdxnzi18BKc7k6PtabVOJur%2FrbW0RMlBQv7x7Q2tCrHKvwCCKX%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMMDM5NjEyODU0NjYxIgyTKhivXu7AHfhM0YQq0AKn6GuFk9N0drJ3bqxL2IrD1djDtFPUL8K4ZOcGPeOClSDZgi2sV8GCiuFvvhVikRMEeT7F3agNKNE7H7jp3qltinuGX3Av65hKgEowqlkenzY3yrCO5EhJqA0VRCOAZ%2BKcr6aIGOKRo7WS8DEJGHvPHu5AXl4pFCp4hLWAzeJVKgLwjtziUYdXHSpxjeR%2FTxSwGF1I5fZawcibkObucgyooouTx6JeXnAxTINMEJzNeTGY%2BHcKcZIMUhaq%2FF%2B%2Bqw60MU9qhsUQ4prvuu2APv8FhDI7U%2FDjYAtGCKhVTbydOvFrdV6vwYPKTYnAdln2fhsLKI%2B4wyb0S8qe13nsTubdPQurkDbJnN1VwFx5xM12wznmaR2qDTEFv0XQ3SAlxUXzVCBDLLHXVs%2Fwc8oUqpB4Ge51fdcSKJV1hHoAzQ%2FhaXaCydqSBuAGfJWfK%2BmSH6MwhNum0AY6rALPrqvfxVTrkjq0Fu6DD1j6jQPO7C6k4mhn3dgzyZJmwRNPLaHl2brlGkJSM127V60d5jh0l3vmE94DG1cG98H01OA%2By8H1BdkD8cCGwq%2BgWWiAHGf2k1lGLAo2anVjlGxuUI0XVeIQoZmRxFcq9h6pLvQnn3N49KKBOF3K9eGe1E3D68Mg4j3UCakrPwt6MEa5vK%2FKsLRLNqG%2BWVsciD5IVEcaWfy5HOyegmUMMjSd2lkZwDlqQOXG6%2BJMb4jnpHQYdf83f4JPfRxH2uAXoD7zeA5k7awPaxvi47Q4sFbB3SSOBkp2bqIh3IMV%2FjcugYg1RkwIujHelldyG2k0VrxSqSXM9cKConIyaKST8gDHRAUI91u4RCjlj0W7q6qyXdgw%2BA3%2BP%2FdZiIcbDhY%3D&X-Amz-Signature=73a84c003b8eadbfaab606fe69b00d20cdae8c96089fd1365f9b6a1b7ef3cdc8&X-Amz-SignedHeaders=host&response-content-disposition=inline
| Parameter | Description | Value |
|---|---|---|
| Endpoint | Base address of the object in S3 | https://compacompia-test.s3.us-east-1.amazonaws.com/avatar.png |
| X-Amz-Algorithm | Encryption algorithm used for the signature | AWS4-HMAC-SHA256 |
| X-Amz-Credential | Who signs, when, and in which region/service | ASIAQSOI4GGCSHG5NDCO%2F20260517%2Fus-east-1%2Fs3%2Faws4_request |
| X-Amz-Content | Payload used, empty for GET requests | UNSIGNED-PAYLOAD |
| X-Amz-Date | UTC date and time of the URL creation | 20260517T120057Z |
| X-Amz-Expires | Validity time in seconds | 300 |
| X-Amz-Security-Token | Token required when using temporary credentials (STS) | IQoJb3JpZ2luX2VjENz…. |
| X-Amz-SignedHeaders | HTTP headers that AWS will validate in the request | host |
| X-Amz-Signature | Cryptographic signature validating the identity | 73a84c003b8eadbfaab…7ef3cdc8 |
With this information, you’ll be able to understand the structure of a presigned URL and how each of its parameters is composed.
When to Use a Presigned URL
Now, after understanding this, you might be wondering if you should always use presigned URLs whenever you need to grant access to a specific object in an S3 bucket. The answer is no, it depends on your specific use case. If access to the object is something that will happen frequently and for a large number of users, then presigned URLs are likely not the best option, as you would have to generate a URL for each user and keep them updated. However, if access to the object is occasional or limited to a small number of users, then presigned URLs can be an excellent choice to maintain your S3 bucket’s security without granting public access. For me, the way to summarize it is as follows:
- If access is static and permanent, you should use bucket policies to grant access to objects.
- If access is dynamic and temporary, presigned URLs are the best option for granting access to objects.
See You Soon
That’s all for now regarding the sixth article in this series, where I’m providing a complete guide on S3 buckets. In the next article, we’ll talk about replication in S3 buckets. See you soon.
Related Content
- Lifecycle Policies in S3 Buckets
- S3 Storage Classes
- What Are S3 Buckets and What Are They For?
- S3 Bucket Policies: Utility and Practical Examples
- S3 Bucket Versioning: Advantages and Implications