How to Show Some Files from a Private S3 Bucket — While Keeping Others Hidden
When building apps that store files like images, PDFs, or videos using Amazon S3, the best practice is to keep your S3 bucket private. But what if you want to: Hide sensitive filesShow only allowed files to specific usersStill serve public assets like profile pictures or logos? Good news: you can do all this securely using pre-signed URLs and smart folder organization. Let’s walk through how. Why You Shouldn’t Make Your S3 Bucket Public While you can make your bucket public, it’s strongly discouraged: Anyone can access your files Search engines might index them No access control Instead, keep the bucket private and generate secure, temporary URLs when users need to access private files. Basic Setup Here’s a typical setup using AWS S3 with a backend and a frontend app: S3 Bucket: private by default Backend (e.g., Strapi or Node.js): handles file uploads and generates access links Frontend (e.g., React Native app): fetches files only for authorized users Organize Your S3 Bucket for Access Control Use prefixes (folders) to separate file types and control access: <pre>s3://my-app-bucket/ ├── public/│ └── avatars/├── clients/│ ├── 001/│ │ └── journal.pdf│ │ └── mood-chart.png│ └── 002/│ └── mood-chart.png├── internal/│ └── admin-reports/</pre> Explanation: /public/ → Used for public files like profile pictures. /clients/001/ → Private files for user ID 001. /internal/ → Admin-only files (e.g., internal reports). This structure makes it easy to control access based on folder paths. How to Show Private Files Securely: Pre-Signed URLs A pre-signed URL is a secure link to a private S3 file that: Expires after a short time Can’t be reused or bookmarked Is safe to send to the frontend Backend Code: Generate Pre-Signed URL (Node.js) const AWS = require(‘aws-sdk’); // Load AWS SDK // Configure AWS S3 with your credentials const s3 = new AWS.S3({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_ACCESS_SECRET, region: process.env.AWS_REGION, }); // Function to generate a pre-signed URL const getPresignedUrl = (key) => { const params = { Bucket: process.env.AWS_BUCKET_NAME, // Your S3 bucket name Key: key, // File path in the bucket Expires: 300, // Expiry time (300 seconds = 5 minutes) }; return s3.getSignedUrl(‘getObject’, params); }; Explanation: getSignedUrl() creates a temporary access link. The Expires value defines how long the link will work (e.g., 5 minutes). This URL can be used to fetch the file securely without making the bucket public. Add This to Your Strapi Controller module.exports = { async getFile(ctx) { const { key } = ctx.query; // Extract file key from query (e.g., clients/001/journal.pdf) // Make sure the user has permission to access the file if (!ctx.state.user || !key.includes(`clients/${ctx.state.user.id}/`)) { return ctx.unauthorized(‘Access denied.’); } // Generate secure, time-limited link const url = getPresignedUrl(key); ctx.send({ url }); }, }; Explanation: ctx.state.user contains the authenticated user. The if check ensures users can only request their own files. If allowed, the signed URL is generated and returned. React Native Frontend Code // Call your backend to get the signed URL const response = await fetch(`${API_URL}/api/get-file?key=clients/001/journal.pdf`); const { url } = await response.json(); // Use the URL to open or display the file Linking.openURL(url); // Opens in browser // OR <Image source={{ uri: url }} /> // Displays image in the app Explanation: You request the file from your backend, not directly from S3. You then use the returned URL temporarily — it expires automatically. This keeps things secure while offering users access when needed. For Public Files: Set Bucket Policy You may have files (like logos or banners) that are okay to show publicly. In that case: Place them in a /public/ folder. Add this policy to your S3 bucket: { “Sid”: “PublicReadGetObject”, “Effect”: “Allow”, “Principal”: “*”, “Action”: “s3:GetObject”, “Resource”: “arn:aws:s3:::my-app-bucket/public/*” } Explanation: This allows anyone to access files under /public/ The rest of the bucket stays private Great for assets like default avatars or app logos Best Practices at a Glance Strategy Why It’s Good Keep your bucket private Prevent accidental leaks Use folder prefixes (/public/, /clients/) Simplifies access control Use pre-signed URLs Secure, short-lived file access Validate user access before signing Prevents sharing someone else’s data Set URL expiry (3–5 mins) Minimizes the risk of misuse Final Thoughts You don’t have to make your S3 bucket public or complex. Instead: Use folder structure to segment access Use pre-signed URLs for sensitive content Use bucket policies for safe public content This approach keeps your app secure, organized, and user-friendly — exactly what you want in a modern mobile or web app. Related reads: How to Set Up an S3 Bucket on AWS (Best Practices for Beginners) Understanding AWS IAM: The Key to Cloud Security for Beginners How to Set Up AWS CLI and IAM for S3 Bucket Access (Beginner-Friendly Guide) External resources: AWS Official Documentation
How to Show Some Files from a Private S3 Bucket — While Keeping Others Hidden Read More »