You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS反向代理AWS S3预签名URL遇SignatureDoesNotMatch错误

问题:NodeJS反向代理S3预签名URL出现SignatureDoesNotMatch错误

我尝试用NodeJS脚本做反向代理,通过生成S3对象的预签名URL来服务静态站点:用户访问build.localhost:8080时,子域名build对应S3里的build文件夹,代理加载该文件夹下的index.html(该文件夹已上传npm run build生成的构建文件)。但运行代码后出现SignatureDoesNotMatch错误,附上代码和错误信息,请求排查。

代码

const express = require('express');
const httpProxy = require('http-proxy');
const AWS = require('aws-sdk');

var credentials = new AWS.SharedIniFileCredentials({profile: 'saml'});
AWS.config.credentials = credentials;

const s3 = new AWS.S3();

function getPreSignedUrl(key){
    const myBucket = 'test-react-bucket-dev';
    const signedUrlExpireSeconds = 60 * 5;
    
    return new Promise((resolve, reject) => {
        s3.getSignedUrl('getObject', {
            Bucket: myBucket,
            Key: key,
            Expires: signedUrlExpireSeconds
        }, (err, url) => {
            if (err) reject(err);
            else resolve(url);
        });
    });
}

const app = express();
const PORT = 8080;

const proxy = httpProxy.createProxyServer();

app.get('*', async (req, res) => {
    const hostname = req.hostname;
    const subdomain = hostname.split('.')[0];

    let key = subdomain + (req.path === '/' ? '/index.html' : req.path);

    try {
        const resolvesTo = await getPreSignedUrl(key);
        proxy.web(req, res, { target: resolvesTo, changeOrigin: true });
    } catch (error) {
        console.error("Error:", error);
        res.status(500).send("Error proxying the request");
    }
});

proxy.on('proxyReq', (proxyReq, req, res) => {
    const url = req.url;
    req.rawHeaders = []
    if (url === '/')
        proxyReq.path += 'index.html';

    console.log(req.rawHeaders)
});

app.listen(PORT, () => console.log(`Reverse Proxy Running..${PORT}`));

错误信息

<Error class="hidden">
<link type="text/css" id="dark-mode" rel="stylesheet" href=""/>
<style type="text/css" id="dark-mode-сustom-style"/>
<style type="text/css" id="dark-mode-theme-changer-style"/>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>ASIAR---REDACTED---EIYB</AWSAccessKeyId>
<StringToSign>GET 1707814358 x-amz-security-token:FwoGZXIvYXdzEKf//////////wEaDDy3nYkyTaxu1hEt6iLnAcXSYnhu0Oq0K7AeEc/quuBjIyqMfZON6gbHFZ3/ZLi7yDwPtdlKsiacTDSKiav1XuQI1WP+aXePJ+RwgtJesC177Jmfg=/index.html /test-react-bucket-jk/build/index.html</StringToSign>
<SignatureProvided>mlC4fkhNSMavJk8R45i6T+QRBr8=</SignatureProvided>
<StringToSignBytes>47 45 54 0a 0a 0a 31 37 30........</StringToSignBytes>
<RequestId>S3T66NPSHTKCDAQ7</RequestId>
<HostId>7EPjXVhzo2S3fBt7fNivJjlnpzcM7Ii9lbsQG4rXYDsoI25+vYvCMS6Cfy1uvVpkHRQVBsqZZI8=</HostId>
</Error>
问题排查与解决方案

核心原因

预签名URL已经包含了完整的请求路径和签名信息,但代理代码在proxyReq事件中修改了请求路径,导致实际发送给S3的请求路径和预签名URL中的路径不一致,签名验证失败。从错误信息的<StringToSign>可以看到,签名是基于/test-react-bucket-jk/build/index.html生成的,但代理后额外添加了/index.html,变成了/index.html/test-react-bucket-jk/build/index.html,完全不符合预期。同时,清空请求头的操作也可能导致S3验证签名时缺少必要信息。

具体修复步骤

  1. 删除proxyReq事件中的所有逻辑
    预签名URL已经包含了正确的对象路径,不需要手动修改路径或清空请求头。直接移除这段代码:

    proxy.on('proxyReq', (proxyReq, req, res) => {
        const url = req.url;
        req.rawHeaders = []
        if (url === '/')
            proxyReq.path += 'index.html';
    
        console.log(req.rawHeaders)
    });
    
  2. 确认S3对象Key的拼接正确性
    当前代码中key = subdomain + (req.path === '/' ? '/index.html' : req.path)的逻辑是正确的,但要确保S3中确实存在对应Key的对象(注意S3的Key区分大小写,且没有真正的文件夹结构,只是用/作为分隔符)。

  3. 可选优化:改用重定向替代反向代理
    不需要用反向代理,直接将预签名URL返回给客户端做重定向更简单可靠,避免代理层篡改请求:

    app.get('*', async (req, res) => {
        const hostname = req.hostname;
        const subdomain = hostname.split('.')[0];
    
        let key = subdomain + (req.path === '/' ? '/index.html' : req.path);
    
        try {
            const signedUrl = await getPreSignedUrl(key);
            res.redirect(signedUrl); // 直接重定向到预签名URL
        } catch (error) {
            console.error("Error:", error);
            res.status(500).send("Error generating signed URL");
        }
    });
    

验证修复

修复后重新运行脚本,访问build.localhost:8080,应该能正确加载S3中的build/index.html文件,不会再出现签名不匹配的错误。

内容的提问来源于stack exchange,提问作者iwrestledthebeartwice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:53:14