PERN栈下配置S3绑定域名或使用EC2实现图片上传访问
Hey there! Let's break down your two main goals here—binding sub.example.com to your S3 bucket for cleaner image URLs, and setting up direct image uploads/storage on EC2. I'll walk you through both step-by-step, tailored for your PERN stack setup.
Since you're already storing images in S3, this is the simpler, more scalable option. Here's how to map your custom domain to S3:
Step 1: Rename your S3 Bucket (Critical!)
S3 requires your bucket name to exactly match your custom domain (sub.example.com). If your current bucket has a different name, create a new one with the correct name and migrate your existing images over (or update your upload logic to use the new bucket).
Step 2: Enable Static Website Hosting on S3
- Go to your S3 bucket's "Properties" tab.
- Scroll to "Static website hosting" and click "Edit".
- Select "Enable", set an index document (e.g.,
index.html—even though you're serving images, this is required), and note the Static website endpoint (it'll look likesub.example.com.s3-website-[region].amazonaws.com). - Save changes.
Step 3: Configure Bucket Permissions for Public Access
You need to make sure images are publicly accessible (but don't overdo it—lock down permissions as much as possible):
- Option A (Recommended): Bucket Policy for Specific Paths
Go to the "Permissions" tab, select "Bucket policy", and paste this (replacesub.example.comwith your bucket name and adjust the path if needed):{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::sub.example.com/uploads/images/*" } ] } - Option B: Auto-Set Public Read on Upload
When uploading images from your Node.js backend, addACL: 'public-read'to your S3 upload params:const uploadParams = { Bucket: 'sub.example.com', Key: `uploads/images/${fileName}`, Body: fileBuffer, ACL: 'public-read' // Add this line };
Step 4: Map Your Domain to S3 via DNS
Head to your domain registrar (or Route 53 if you use AWS for DNS) and add a CNAME record:
- Hostname:
sub.example.com - Value: The S3 static website endpoint you noted earlier (omit
http://orhttps://)
Step 5 (Optional): Add HTTPS with CloudFront
S3's static website doesn't support HTTPS for custom domains natively, so use CloudFront to add encryption and caching:
- Create a new CloudFront distribution:
- Origin domain: Paste your S3 static website endpoint
- Origin path: Leave blank
- Viewer protocol policy:
Redirect HTTP to HTTPS - Alternate domain names (CNAMEs):
sub.example.com - Custom SSL certificate: Use AWS Certificate Manager (ACM) to request a free certificate for
sub.example.com(must be in theus-east-1region for CloudFront)
- Update your DNS CNAME record to point to the CloudFront distribution domain (instead of the S3 endpoint)
- Now your image URLs will be
https://sub.example.com/uploads/images/image1.png
Update Your PERN Backend
Instead of storing the default S3 URL, construct your custom URL using the image's S3 key:
// After uploading to S3, get the key (e.g., 'uploads/images/image1.png') const customImageUrl = `https://sub.example.com/${s3Key}`; // Save this URL to your PostgreSQL database await pool.query('INSERT INTO images (url) VALUES ($1)', [customImageUrl]);
If you prefer to handle storage directly on EC2, here's how to set this up securely and reliably:
Step 1: Prepare EC2 Storage
Avoid using EC2's temporary instance storage (data is lost on reboot)—use an EBS volume instead:
- Attach an EBS volume to your EC2 instance (size based on your storage needs)
- Mount the volume to a directory like
/var/www/uploads/images:sudo mkdir -p /var/www/uploads/images sudo mount /dev/xvdf /var/www/uploads/images # Replace xvdf with your EBS device name # Add to fstab to auto-mount on reboot echo '/dev/xvdf /var/www/uploads/images ext4 defaults 0 0' | sudo tee -a /etc/fstab - Set permissions so your Node.js process can write to the directory:
sudo chown -R $USER:$USER /var/www/uploads/images sudo chmod -R 755 /var/www/uploads/images
Step 2: Configure Express to Handle Uploads
Use the multer middleware to handle file uploads in your Node.js backend:
- Install multer:
npm install multer - Set up multer storage in your Express app:
const multer = require('multer'); // Configure storage destination and filename logic const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, '/var/www/uploads/images'); }, filename: (req, file, cb) => { // Generate a unique filename to avoid overwrites const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); const fileExt = file.originalname.split('.').pop(); cb(null, `image-${uniqueSuffix}.${fileExt}`); } }); // Filter to only allow image files (optional but recommended) const fileFilter = (req, file, cb) => { if (file.mimetype.startsWith('image/')) { cb(null, true); } else { cb(new Error('Only image files are allowed!'), false); } }; const upload = multer({ storage: storage, fileFilter: fileFilter }); - Create an upload route:
app.post('/api/upload-image', upload.single('image'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No image file provided' }); } // Construct the public URL const imageUrl = `https://sub.example.com/uploads/images/${req.file.filename}`; // Save to PostgreSQL await pool.query( 'INSERT INTO images (url, filename) VALUES ($1, $2) RETURNING *', [imageUrl, req.file.filename] ); res.status(200).json({ url: imageUrl }); } catch (err) { console.error('Upload error:', err); res.status(500).json({ error: 'Failed to upload image' }); } });
Step 3: Set Up Nginx to Serve Static Images & Proxy API Requests
Using Nginx is safer than exposing Express directly to the internet—it handles static files efficiently and adds a layer of security:
- Install Nginx on your EC2 instance:
sudo apt update && sudo apt install nginx -y # For Ubuntu/Debian # Or for Amazon Linux: sudo yum install nginx -y - Create a new Nginx config file for
sub.example.com:sudo nano /etc/nginx/sites-available/sub.example.com - Paste this config (adjust port if your Express app runs on something other than 3000):
server { listen 80; server_name sub.example.com; # Serve static images from EBS volume location /uploads/images/ { root /var/www; expires 30d; # Cache images for 30 days to reduce load add_header Cache-Control "public, max-age=2592000"; } # Proxy API requests to Express location /api/ { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # Redirect root to your frontend (optional) location / { proxy_pass http://your-frontend-url.com; # Or point to static frontend files } } - Enable the config and restart Nginx:
sudo ln -s /etc/nginx/sites-available/sub.example.com /etc/nginx/sites-enabled/ sudo nginx -t # Test config for errors sudo systemctl restart nginx
Step 4: Add HTTPS with Let's Encrypt
Get a free SSL certificate to enable HTTPS:
- Install Certbot:
sudo apt install certbot python3-certbot-nginx -y # Ubuntu/Debian # Amazon Linux: sudo yum install certbot python3-certbot-nginx -y - Request and install the certificate:
Follow the prompts—Certbot will auto-update your Nginx config to redirect HTTP to HTTPS.sudo certbot --nginx -d sub.example.com
Step 5: Configure EC2 Security Group
Make sure your EC2 security group allows inbound traffic on ports 80 (HTTP) and 443 (HTTPS) from all sources (or restrict to your frontend IP if needed).
Key Notes for EC2 Setup
- Backup EBS Volumes: Regularly create snapshots of your EBS volume to prevent data loss.
- Scalability: EC2 storage isn't as scalable as S3—if you expect high traffic or large storage needs, consider syncing images to S3 in the background.
- Performance: For large image libraries, add a CDN (like CloudFront) to cache images globally.
Which Option Should You Choose?
- Go with S3 + Custom Domain if you want low maintenance, high scalability, and built-in reliability. It's the AWS-native approach and perfect for most image storage use cases.
- Go with EC2 only if you need full control over the storage environment, or have specific requirements that S3 doesn't meet (like custom processing on upload that's easier to run on EC2).
内容的提问来源于stack exchange,提问作者Hamza Zeb

