Skip to content

[Bug] .htaccess relative RewriteRule leaks internal port :8080 into 301 Location header when run behind a reverse proxy #200

@ThomDietrich

Description

@ThomDietrich

Acknowledgements

  • This is a bug and not a question. Questions should be asked in the Discussions area.
  • This project is maintained 100% by volunteers. I understand that there are no guarantees on when (or if) this will be addressed, and that the most effective way to see this resolved is for me to submit a Pull Request.

Describe the bug

The librebooking/librebooking:develop Docker image listens on port 8080 internally (the image's build script rewrites Apache's Listen 80Listen 8080), but ships a /var/www/html/.htaccess whose catch-all redirect uses a relative target:

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/Web(/|$)
RewriteRule ^(.*)$ /Web/$1 [R=301,L]

When Apache emits this as an external 301, it has to expand /Web/$1 into an absolute URL for the Location: header. With UseCanonicalName Off (the default) and no explicit ServerName set in the image's vhost, Apache builds the URL as http://<Host-header-hostname>:<physical-listen-port>/… — i.e. http://<host>:8080/Web/.

For any deployment behind a reverse proxy (traefik, nginx-proxy, Caddy, ...) where port 8080 is not exposed externally, browsers receive a redirect they cannot follow, and the app is unusable on its landing page.

To Reproduce

Minimal compose fragment (traefik as the proxy — same bug appears with any reverse proxy that doesn't preserve the internal port):

services:
  app:
    image: librebooking/librebooking:develop
    labels:
      traefik.enable: true
      traefik.http.routers.lb.rule: Host(`booking.example.com`)
      traefik.http.routers.lb.tls.certresolver: my-resolver
      traefik.http.services.lb.loadbalancer.server.port: 8080

Observed:

$ curl -sSI https://booking.example.com/
HTTP/2 301
location: http://booking.example.com:8080/Web/
server: Apache/2.4.66 (Debian)

Expected behavior

location: https://booking.example.com/Web/

Screenshots

No response

Additional context

Why this bites now

Setting .htaccess's RewriteRule target as a relative path was fine when Apache listened on port 80 — the port gets elided from redirect URLs (80 is the default for HTTP), and a proxy's http→https redirect handles the scheme upgrade. Since the image now listens on 8080, Apache dutifully includes :8080 in the Location, which is invisible to the outside world and cannot be followed.

The app's own script.url in config.php doesn't help: this 301 is emitted at the Apache/.htaccess layer before any PHP runs.

Workaround for other users hitting this

Bind-mount a .htaccess that emits an absolute HTTPS URL using %{HTTP_HOST}:

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/Web(/|$)
RewriteRule ^(.*)$ https://%{HTTP_HOST}/Web/$1 [R=301,L]
volumes:
  - ./htaccess-override:/var/www/html/.htaccess:ro

Assumes the proxy terminates TLS (the typical behind-proxy deployment) and forwards Host: without a port (traefik/nginx/Caddy defaults).

Possible upstream fixes

A few directions — not prescriptive, just for discussion:

  1. Emit an absolute URL in the shipped .htaccess with scheme detection from X-Forwarded-Proto:
    RewriteCond %{HTTP:X-Forwarded-Proto} =https [OR]
    RewriteCond %{HTTPS} =on
    RewriteRule ^(.*)$ https://%{HTTP_HOST}/Web/$1 [R=301,L]
    
    RewriteCond %{HTTP:X-Forwarded-Proto} !=https
    RewriteCond %{HTTPS} !=on
    RewriteRule ^(.*)$ http://%{HTTP_HOST}/Web/$1 [R=301,L]
  2. Set UseCanonicalName On + a configurable ServerName via an env var (e.g. LB_SERVER_NAME).
  3. Trust X-Forwarded-* in the image's Apache config. The image already ships remoteip.conf with RemoteIPHeader X-Real-IP, but doesn't tell Apache the forwarded scheme. Adding SetEnvIf X-Forwarded-Proto "^https$" HTTPS=on + UseCanonicalPhysicalPort Off might be enough.
  4. Reconsider Listen 8080. If the motivation is running Apache as a non-root user, CAP_NET_BIND_SERVICE on the container (or the existing www-data + authbind-style trick) works on port 80 too. Going back to Listen 80 sidesteps the whole class of "physical-port-leaks-into-URL" issues.

LibreBooking version

librebooking/librebooking:develop, Image ID: sha256:238a02e1d4bd3e5d3621ebe93def8057a131debdc462b098ded49a48e45b40bd - Image created: 2026-04-14T04:18:57Z - Apache: 2.4.66 (Debian) - Deployment: behind traefik v3 with TLS termination (reproducible with any similar reverse proxy)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions