302 Found
What is HTTP 302 Found?
When you ask a website for a page, sometimes that page has moved somewhere else ...
Explain Like I’m 3
The thing you’re looking for isn’t here right now, but it’s over there for a little bit! Later it will come back here. It’s like when your toy is in the toy box, but someone is playing with it for a little while - you go get it from them instead!
Example: Your favorite book is usually on the shelf, but today it’s on the table. Someone tells you ‘Go look on the table!’ But tomorrow it will be back on the shelf again.
Explain Like I’m 5
When you ask a website for a page, sometimes that page has moved somewhere else temporarily. The website says ‘302 Found - what you want is temporarily at this other address!’ Your browser automatically goes to the new address to get what you wanted. But the page will come back to its original address later - maybe after a sale ends, or when maintenance is done. It’s like when your classroom temporarily moves to the gym for a special event - you still have class, just in a different place for now, but tomorrow you’ll be back in your regular classroom.
Example: You type in a website for a store. It’s having a special holiday sale, so instead of the normal homepage, the website automatically takes you to the ‘Holiday Sale’ page. After the sale ends in a few days, the website will go back to showing the normal homepage.
Jr. Developer
HTTP 302 Found is a temporary redirect status code indicating the requested resource has been temporarily moved to the URL specified in the Location header. Unlike 301 Permanent Redirect, 302 tells clients and search engines the move is temporary and the original URL should be used for future requests. Important: 302 allows the HTTP method to change on redirect - browsers historically changed POST requests to GET when following 302 redirects, even though the spec originally said not to. If you need to preserve the method (keep POST as POST), use 307 Temporary Redirect instead. Common uses: A/B testing, seasonal promotions, maintenance redirects, geolocation-based redirects. SEO note: 302 redirects don’t pass link equity to the new URL like 301 does - search engines continue indexing the original URL.
Example: Your e-commerce site is running a Black Friday sale. You temporarily redirect the homepage (/) to the sale page (/black-friday-sale) with a 302. After the sale ends, you remove the redirect and the homepage returns to normal. Search engines continue ranking the homepage, not the temporary sale page.
Code Example
// Express.js implementing 302 redirectapp.get('/', (req, res) => { const isBlackFriday = checkIfBlackFriday();
if (isBlackFriday) { // Temporary redirect to sale page return res.redirect(302, '/black-friday-sale'); // or simply: res.redirect('/sale') - 302 is default }
// Normal homepage res.render('home');});
// Maintenance mode redirectapp.use((req, res, next) => { const maintenanceMode = process.env.MAINTENANCE === 'true';
if (maintenanceMode && req.path !== '/maintenance') { return res.redirect(302, '/maintenance'); }
next();\n});
// Note: POST to GET conversionapp.post('/form-submit', (req, res) => { // If you redirect POST with 302, browsers change it to GET! res.redirect(302, '/thank-you'); // Browser will GET /thank-you
// To preserve POST method, use 307 instead: // res.redirect(307, '/thank-you'); // Browser will POST /thank-you});Crash Course
302 Found, defined in RFC 9110 Section 15.4.3 (obsoleting RFC 7231), indicates the target resource temporarily resides at a different URI and future requests should continue using the original URI. The server MUST send a Location header with the redirect target. Historical quirk: HTTP/1.0 spec said clients should preserve the method when redirecting, but browsers ignored this and changed POST to GET. HTTP/1.1 formalized this behavior for 302 while adding 307 Temporary Redirect (which guarantees method preservation) for strict compliance. Today: 302 allows method change (typically POST→GET), 307 forbids it. Use 302 for page redirects where method doesn’t matter, 307 when you need POST/PUT/DELETE to remain POST/PUT/DELETE after redirect. SEO implications: 302 doesn’t transfer PageRank or link equity - search engines treat it as temporary and continue indexing the original URL. Google may show either URL in results but generally prefers the original. Best for: A/B testing, seasonal campaigns, maintenance pages, geolocation-based routing, preview/staging environments. Not recommended for: permanent URL changes (use 301), API redirects requiring method preservation (use 307), situations where you want to pass SEO value (use 301).
Example: A SaaS application redirects unauthenticated users from /dashboard to /login using 302. Once logged in, users can access /dashboard normally. The redirect is temporary because whether it applies depends on authentication state. If 301 were used, browsers and search engines would think /dashboard permanently moved to /login, breaking the app for authenticated users and harming SEO.
Code Example
// Comprehensive 302 usage patternsconst express = require('express');const app = express();
// Authentication redirect (common use case)function requireAuth(req, res, next) { if (!req.session.userId) { // Temporary redirect - dashboard still exists, just need auth return res.redirect(302, `/login?return=${encodeURIComponent(req.path)}`); } next();}
app.get('/dashboard', requireAuth, (req, res) => { res.render('dashboard');});
// A/B testing redirectapp.get('/pricing', (req, res) => { const variant = Math.random() < 0.5 ? 'A' : 'B';
if (variant === 'B') { // Temporarily redirect 50% to new pricing page return res.redirect(302, '/pricing-v2'); }
res.render('pricing-original');});
// Geolocation-based redirectapp.get('/', (req, res) => { const country = req.headers['cf-ipcountry']; // Cloudflare header
// Temporarily redirect based on location if (country === 'GB') { return res.redirect(302, '/en-gb'); } else if (country === 'DE') { return res.redirect(302, '/de'); }
res.render('home-us'); // Default US version});
// Seasonal promotion redirectapp.get('/products/:id', async (req, res) => { const now = new Date(); const isHolidaySeason = now.getMonth() === 11; // December
if (isHolidaySeason) { // Temporarily show holiday version return res.redirect(302, `/holiday-products/${req.params.id}`); }
const product = await db.getProduct(req.params.id); res.render('product', { product });});
// CAUTION: Method change behaviorapp.post('/submit-form', (req, res) => { // Save form data db.saveFormData(req.body);
// This redirects with 302 (default) - browser changes POST to GET res.redirect('/thank-you'); // Browser will GET /thank-you
// If /thank-you endpoint expects POST, use 307: // res.redirect(307, '/thank-you'); // Browser will POST /thank-you});Deep Dive
The 302 Found status code, specified in RFC 9110 Section 15.4.3, indicates the target resource temporarily resides under a different URI. Agents SHOULD use the URI provided in the Location header for this request but continue to use the effective request URI for future requests. The server MUST generate a Location header containing a different URI reference. If the Location value is a relative reference, it must be resolved relative to the effective request URI. Per RFC 9110, the 302 response is cacheable by default unless otherwise indicated by method definition or explicit cache controls, but in practice most implementations don’t cache 302s without explicit headers due to their temporary nature.
Technical Details
Historical evolution explains 302’s quirky method-changing behavior: HTTP/1.0 (RFC 1945) defined status code 302 “Moved Temporarily” and specified the client should perform a temporary redirect to the Location URI using the same method. However, all major browsers (Netscape, IE, early Chrome/Firefox) implemented POST-to-GET conversion for 302 redirects, likely for usability - after submitting a form, users expect to see a new page (GET), not resubmit the form (POST). HTTP/1.1 (RFC 2068) acknowledged this de facto standard and introduced 303 See Other (explicitly converts POST to GET) and 307 Temporary Redirect (strictly preserves method). RFC 2616 and later RFC 7231/9110 formalized that 302 allows method change (POST/PUT/DELETE may become GET), making the browser behavior specification-compliant. Today’s landscape: 302 permits but doesn’t require method change (implementation-defined), 303 mandates POST→GET conversion, 307 forbids method change.\n\nCaching semantics for 302 are complex: RFC 9110 states 302 is cacheable by default, but RFC 9111 (HTTP Caching) clarifies that since 302 is temporary, caching without explicit freshness information is problematic. Most browsers and CDNs don’t cache 302 responses unless Cache-Control: max-age or Expires headers are present. If you do cache 302s, the cached redirect affects all users - a cached geolocation redirect might send everyone to the German site instead of respecting individual users’ locations. Best practice: include Cache-Control: no-store for dynamic 302s (auth, geolocation), or Cache-Control: public, max-age=300 for short-lived cacheable redirects (A/B testing, rate limiting).\n\nSEO implications are significant and often misunderstood: Google’s John Mueller confirms that while Google eventually treats long-lived 302s like 301s, this takes time (months) and isn’t reliable. 302 redirects don’t pass PageRank or anchor text - the original URL retains ranking signals. Use cases where this is desired: A/B testing (don’t want test page to inherit rankings), temporary maintenance (want original page to stay in index), seasonal promotions (don’t want promo page to replace main page in SERPs). Mistakes: using 302 for permanent migrations (delays PageRank transfer), creating 302 chains (compounds delay and confusion), leaving 302s in place long-term without updating to 301. Google Search Console reports “Redirect error” if 302s redirect to soft 404s or the chain exceeds 5 hops.\n\nSecurity considerations include open redirect vulnerabilities when the redirect target comes from user input. Example: /login?return=/dashboard is safe, but /login?return=https://evil.com creates an open redirect if not validated. Mitigation: whitelist allowed redirect domains, use relative paths only, validate the return parameter against known internal routes. Session fixation attacks can exploit authentication redirects - ensure session ID regeneration after login before the 302 redirect. Phishing attacks abuse trusted…
Code Example
// Production-grade 302 redirect implementation\nconst express = require('express');\nconst crypto = require('crypto');\nconst url = require('url');\nconst app = express();\n\n// Secure redirect helper with open redirect protection\nfunction safeRedirect(res, targetUrl, options = {}) {\n const {\n permanent = false,\n preserveMethod = false,\n cache = false,\n allowedDomains = [process.env.APP_DOMAIN]\n } = options;\n \n // Parse target URL\n let parsedUrl;\n try {\n parsedUrl = new url.URL(targetUrl, \`https://${process.env.APP_DOMAIN}\`);\n } catch (err) {\n // Invalid URL - default to safe fallback\n return res.redirect(302, '/');\n }\n \n // Validate domain to prevent open redirects\n if (!allowedDomains.includes(parsedUrl.hostname)) {\n console.warn(\`Blocked redirect to untrusted domain: ${parsedUrl.hostname}\`);\n return res.redirect(302, '/');\n }\n \n // Determine status code\n let statusCode = 302; // Default temporary, allows method change\n if (permanent) {\n statusCode = preserveMethod ? 308 : 301;\n } else if (preserveMethod) {\n statusCode = 307;\n }\n \n // Set caching headers\n if (cache) {\n res.set('Cache-Control', \`public, max-age=${cache}\`);\n } else {\n res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');\n res.set('Pragma', 'no-cache');\n res.set('Expires', '0');\n }\n \n return res.redirect(statusCode, targetUrl);\n}\n\n// Authentication redirect with return URL\napp.get('/dashboard', (req, res) => {\n if (!req.session?.userId) {\n // Temporary redirect - preserve return path\n const returnUrl = encodeURIComponent(req.originalUrl);\n return safeRedirect(res, \`/login?return=${returnUrl}\`);\n }\n \n res.render('dashboard', { user: req.session.user });\n});\n\napp.post('/login', async (req, res) => {\n const { username, password } = req.body;\n const user = await authenticateUser(username, password);\n \n if (!user) {\n return res.status(401).render('login', { error: 'Invalid credentials' });\n }\n \n // CRITICAL: Regenerate session ID to prevent fixation\n req.session.regenerate((err) => {\n if (err) {\n return res.status(500).json({ error: 'Session error' });\n }\n \n req.session.userId = user.id;\n req.session.user = user;\n \n // Validate and use return URL\n const returnUrl = req.query.return || '/dashboard';\n safeRedirect(res, returnUrl);\n });\n});\n\n// A/B testing redirect with consistent variant assignment\napp.get('/pricing', (req, res) => {\n // Use user ID or session ID to ensure consistent variant\n const userId = req.session?.userId || req.sessionID;\n const hash = crypto.createHash('md5').update(userId).digest('hex');\n const variant = parseInt(hash.substring(0, 8), 16) % 2 === 0 ? 'A' : 'B';\n \n // Track variant assignment\n req.session.pricingVariant = variant;\n \n if (variant === 'B') {\n // Temporary redirect to variant B\n // Don't cache ...Frequently Asked Questions
What's the difference between 302 Found and 301 Moved Permanently?
302 is for temporary redirects where the original URL will be used again (A/B tests, maintenance, seasonal promotions), while 301 is for permanent moves where the old URL should never be used again. SEO-wise, 301 transfers PageRank and link equity to the new URL; 302 keeps it on the original URL. Use 301 when migrating content permanently, 302 when the redirect is temporary.
Why does my POST request become a GET after a 302 redirect?
This is historical browser behavior that became standardized. Originally HTTP/1.0 spec said to preserve the method, but all browsers changed POST to GET for usability. HTTP/1.1 formalized this behavior for 302. If you need to preserve the POST method, use 307 Temporary Redirect instead of 302, which guarantees the method won't change.
Should I use 302 or 307 for temporary redirects?
Use 302 for page redirects where method doesn't matter (homepage to sale page, unauthenticated users to login). Use 307 when you need to preserve the HTTP method - for API redirects where POST must stay POST, or form submissions where you need the request body to carry through. For most web page redirects, 302 is fine and more widely supported.
Are 302 redirects bad for SEO?
No, 302 redirects are appropriate for temporary situations and won't harm SEO when used correctly. The original URL keeps its rankings and link equity, which is exactly what you want for temporary changes. They become problematic only when: (1) used for permanent migrations (should be 301), (2) left in place for months/years (Google may eventually treat as 301), or (3) creating redirect chains.
How can I prevent open redirect vulnerabilities with 302?
Validate all redirect targets: (1) Use a whitelist of allowed domains, (2) Only allow relative paths (starting with /), not absolute URLs, (3) Validate return URLs against known internal routes, (4) Never directly redirect to user-provided URLs without validation. For return URLs after login, encode them (encodeURIComponent), validate they're internal paths, and default to a safe page like /dashboard if validation fails.
Common Causes
- Redirecting unauthenticated users to login page temporarily
- A/B testing where users are redirected to experimental page variant
- Seasonal promotions or sales redirecting from main page to promotion page
- Geolocation-based redirects sending users to country-specific versions
- Maintenance mode redirecting all requests to maintenance page temporarily
- Feature flag redirects sending users without access to alternative version
- Rate limiting redirects sending exceeded users to rate limit info page
- Temporary content moves where original URL will be restored later
Implementation Guidance
- For permanent URL changes, use 301 Permanent Redirect instead of 302
- If POST method must be preserved, use 307 Temporary Redirect instead
- Always validate redirect targets to prevent open redirect vulnerabilities
- Use relative paths in Location header when possible for better security
- Add Cache-Control: no-store for dynamic redirects (auth, geolocation)
- Include Retry-After header for maintenance or rate-limit redirects
- Set appropriate Vary headers (Vary: CF-IPCountry) for cached location-based redirects
- Avoid redirect chains - if A redirects to B, don’t make B redirect to C
- Regenerate session IDs after login before redirecting to prevent fixation
- Monitor long-lived 302s - update to 301 if the change becomes permanent