Apache
HTTP
X-Forwarded-Proto
.htaccess
redirect loop

apache HTTPX-Forwarded-Proto in .htaccess is causing redirect loop in dev environment

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Redirect loops with X-Forwarded-Proto usually happen when your app or rewrite rules disagree about whether the original request was HTTP or HTTPS. This is common in development stacks where proxy behavior differs from production. The fix is to trust forwarded headers only in the right environment and define one canonical redirect rule.

Why the Loop Happens

A typical loop sequence is:

  1. Client requests HTTP.
  2. Proxy forwards to Apache and sets or omits X-Forwarded-Proto.
  3. .htaccess forces HTTPS based on current condition.
  4. Upstream rewrites again because it still thinks request is HTTP.
  5. Browser repeats until too many redirects.

In dev, this often occurs when local reverse proxy does not set headers exactly like production load balancer.

Safe Rewrite Pattern in .htaccess

Use a single redirect condition that checks both direct HTTPS and forwarded protocol.

apache
1RewriteEngine On
2
3# Redirect to HTTPS only when request is not already secure
4RewriteCond %{HTTPS} !=on
5RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
6RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

This prevents double-redirect logic when proxied requests are already HTTPS externally.

Ensure Proxy Sends Correct Header

If Apache is behind Nginx, set forwarded headers explicitly.

nginx
1location / {
2    proxy_pass http://127.0.0.1:8080;
3    proxy_set_header Host $host;
4    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
5    proxy_set_header X-Forwarded-Proto $scheme;
6}

For Apache reverse proxy fronting another backend, pass equivalent headers in vhost config.

apache
RequestHeader set X-Forwarded-Proto "https" env=HTTPS

Consistency across all proxy layers is critical.

Development Environment Strategy

In local development, you may not need forced HTTPS at all. You can scope redirect rules to production hostnames.

apache
1RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
2RewriteCond %{HTTPS} !=on
3RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
4RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

This avoids loops on local hostnames where TLS is not configured.

Debugging Checklist

Validate each step before changing multiple rules at once.

bash
curl -I http://localhost
curl -I -H "X-Forwarded-Proto: https" http://localhost

Inspect response status and Location headers. If both requests redirect, condition logic is too broad. If neither redirects in production, proxy headers may be missing.

Enable rewrite logging at server level during troubleshooting to see which rule matched.

Protect Against Header Spoofing

Do not trust X-Forwarded-Proto from arbitrary direct internet clients. Only trust it when traffic comes from your known proxy network.

At proxy layer, strip incoming forwarded headers and set your own trusted value.

nginx
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

At application layer, configure trusted proxies so framework URL generation uses the expected scheme. Otherwise, apps may emit HTTP links and trigger repeated redirects even when Apache rules are correct.

Prefer Server Config Over .htaccess for Complex Rules

When possible, move rewrite logic from .htaccess to virtual host config. Server-level config is faster, easier to audit, and less likely to conflict with nested directory rules.

A clean setup is:

  • Proxy or load balancer terminates TLS.
  • One canonical HTTPS redirect rule at edge or vhost layer.
  • Application trusts forwarded headers only from known proxy hops.

This architecture prevents most redirect loops before they reach application code.

Common Pitfalls

  • Forcing HTTPS both at proxy layer and .htaccess without shared conditions.
  • Trusting X-Forwarded-Proto from untrusted direct client traffic.
  • Applying production redirect rules unchanged in local environment.
  • Using multiple overlapping rewrite files in nested directories.
  • Forgetting to clear browser redirect cache after rule updates.

Summary

  • Redirect loops come from inconsistent protocol detection across proxy and Apache layers.
  • Use one canonical rewrite condition that checks both HTTPS and X-Forwarded-Proto.
  • Ensure proxies set forwarded protocol headers consistently.
  • Scope HTTPS-forcing rules to appropriate environments when needed.
  • Debug with curl and response headers before broad rewrite changes.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design