重定向就像快递员送快递时发现地址变更,新地址会写在包裹单的"改派地址"栏。
浏览器收到3xx状态码时,会自动前往Location头指定的新地址。
常用状态码对比:
// Express基础示例
const express = require('express');
const app = express();
// 301永久重定向(SEO权重转移)
app.get('/old-page', (req, res) => {
res.redirect(301, '/new-page'); // 必须显式指定301
});
// 302临时重定向(默认行为)
app.get('/temp-redirect', (req, res) => {
res.redirect('/new-location'); // 默认302
});
// 307/308精准重定向(保持请求方法)
app.get('/strict-redirect', (req, res) => {
res.status(307).header('Location', '/new-location').send();
});
现代浏览器默认自动跟随重定向,但特殊场景需要手动处理:
// Fetch API手动处理重定向
fetch('/api/data', { redirect: 'manual' })
.then(response => {
if (response.status === 301 || response.status === 302) {
const newUrl = response.headers.get('Location');
window.location.href = newUrl; // 需处理相对路径
} else {
return response.json();
}
})
.catch(error => console.error('Redirect failed:', error));
301永久重定向:
302临时重定向:
// 错误:未处理相对路径
const newUrl = '/new-path'; // 如果原地址是 /old/path
// 正确应解析为绝对路径
function resolveRedirect(base, path) {
return new URL(path, base).href;
}
// 示例用法
const absoluteUrl = resolveRedirect('https://example.com/old/', '../new');
// 输出: https://example.com/new
// 服务端防止循环重定向
const MAX_REDIRECTS = 5;
app.use((req, res, next) => {
const redirectHistory = req.session.redirectHistory || [];
if (redirectHistory.length >= MAX_REDIRECTS) {
return res.status(508).send('Loop Detected');
}
res.redirect = function(url, status = 302) {
redirectHistory.push(req.originalUrl);
req.session.redirectHistory = redirectHistory;
res.status(status).set('Location', url).end();
};
next();
});
// 307重定向保持请求方法
app.post('/submit-form', (req, res) => {
if (needsRedirect(req.body)) {
res.status(307)
.set('Location', '/new-submit-url')
.send();
} else {
// 正常处理逻辑
}
});
# Nginx配置301缓存
location /legacy-url {
return 301 /new-url;
add_header Cache-Control "public, max-age=31536000"; # 缓存1年
}
// 分布式环境下同步重定向规则
const redis = require('redis');
const client = redis.createClient();
app.get('/dynamic-redirect/:id', async (req, res) => {
const target = await client.get(`redirect:${req.params.id}`);
target ? res.redirect(302, target) : res.sendStatus(404);
});
// 校验目标URL合法性
const allowedDomains = ['example.com', 'trusted-site.org'];
function isValidRedirect(url) {
try {
const parsed = new URL(url);
return allowedDomains.includes(parsed.hostname);
} catch {
return false;
}
}
app.get('/redirect', (req, res) => {
const target = req.query.url;
if (isValidRedirect(target)) {
res.redirect(302, target);
} else {
res.status(400).send('Invalid redirect target');
}
});
// 删除敏感信息再重定向
app.use('/auth-callback', (req, res) => {
const cleanUrl = new URL(req.originalUrl);
cleanUrl.searchParams.delete('token');
res.redirect(cleanUrl.toString());
});
# 查看完整重定向过程
curl -Lv http://example.com/old-url
# 限制最大重定向次数
curl --max-redirs 3 http://example.com
# 保持POST方法测试(-X POST -d参数)
curl -X POST -d "param=value" -Lv http://example.com/submit
// 捕获重定向错误
window.addEventListener('error', (event) => {
if (event.message.includes('redirect')) {
trackError('Redirect Error', {
url: window.location.href,
destination: event.filename
});
}
});
// 性能监控重定向耗时
const navigationTiming = performance.getEntriesByType('navigation')[0];
if (navigationTiming.redirectCount > 0) {
reportMetrics({
redirectCount: navigationTiming.redirectCount,
totalTime: navigationTiming.redirectEnd - navigationTiming.startTime
});
}
// 终极防御:全链路重定向处理
const fetchWithGuard = async (url, options = {}) => {
let redirectCount = 0;
const controller = new AbortController();
const fetchData = async (url) => {
if (redirectCount >= 5) throw new Error('Too many redirects');
const response = await fetch(url, {
...options,
signal: controller.signal,
redirect: 'manual'
});
if ([301, 302, 307, 308].includes(response.status)) {
redirectCount++;
const newUrl = response.headers.get('Location');
return fetchData(new URL(newUrl, url).href);
}
return response;
};
return fetchData(url);
};
正确运用重定向技术可以实现:
但需时刻警惕: