Homarr
Advanced

Proxies and Certificates

Allowing self-signed certificates

Some users may come across a barrier, where they're unable to receive a 200 response from the Ping widget for some apps, while using self-signed certificates or a local certificate authory.

What's going on?

Homarr is trying to communicate to your apps via the integrations. It usually doesn't matter if Homarr is running on http or https. Your apps have a self-signed certificate - Homarr will recognize that the certificate was signed by an unknown authority and requests will be blocked.

To allow self-signed certificates, you can configure them on the certificates management page. You can find more informations on the dedicated page: Certificates

Trusted request identity

Bundled nginx attributes OAuth abuse-control requests to its TCP peer. Client-supplied forwarding headers do not establish trust. Raw Next.js access without an authenticated proxy shares an unattributed budget: 5 registrations and 10 failed token requests per minute. Use the bundled port 7575, not the internal application port.

Behind an external reverse proxy, set HOMARR_TRUSTED_PROXIES to that proxy's exact IP addresses or narrowly scoped CIDRs, separated by commas. No external proxies are trusted by default. For example, HOMARR_TRUSTED_PROXIES=172.30.0.2/32,172.30.0.3/32 supports two known proxies. Every trusted hop must overwrite incoming X-Forwarded-For at the public edge or append its actual TCP peer; nginx walks the chain from the right to the last untrusted address. Never trust a network containing untrusted clients or containers. An unconfigured proxy safely shares its own budget rather than trusting its forwarded addresses.

For an external nginx directly in front of bundled Homarr:

location / {
    proxy_pass http://homarr:7575;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Allowlist this nginx instance's address in HOMARR_TRUSTED_PROXIES. Additional private proxy hops must append their TCP peer and be individually allowlisted. Restrict access to the proxy network, and use BASE_URL=https://your.public.hostname for TLS termination; identity trust does not change consent Origin checks or canonical URL handling.

Non-container deployments may set HOMARR_REQUEST_IDENTITY_SECRET to a random 64-character hex secret shared only with their trusted proxy. That proxy must overwrite X-Homarr-Proxy-Secret with the secret and X-Homarr-Client-IP with its validated single client IP, using the same explicit peer allowlist. Keep the upstream network private and never log or return the secret. Bundled startup generates its own secret on each start; no manual secret configuration is needed.

OAuth limits are process-local, with one-minute windows: registration 5 per identity and 50 total, token requests 60 per identity and 300 total, and failed token requests 10 per identity. Multi-instance deployments need an additional shared edge limit.

Securing Homarr with Traefik

Copying the configuration straight from the docker-compose file won't work if you are running Homarr behind Traefik, such as a Portainer setup, or docker-swarm. In that case, you should use the following slightly modified configuration:

version: "3"
services:
  homarr:
    container_name: homarr
    image: ghcr.io/homarr-labs/homarr:latest
    restart: unless-stopped
    volumes:
      - ./homarr/appdata:/appdata
    environment:
      - BASE_URL=https://your.internal.dns.address.here.com
      - SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
    networks:
      - proxy
    labels:
      traefik.enable: true
      traefik.http.routers.homarr.rule: Host(`your.internal.dns.address.here.com`)
      traefik.http.routers.homarr.entrypoints: websecure
      traefik.http.routers.homarr-secure.app: homarr

networks:
  proxy:
    external: true

A sample Traefik docker-compose.yml using Cloudflare for certificate generation that works with the configuration above would be:

version: "3"

apps:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    environment:
      - [email protected]
      - CF_DNS_API_TOKEN=long-token-from-cf

    command:
      - "--log.level=DEBUG"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls.certResolver=cloudflare"
      - "--certificatesresolvers.cloudflare.acme.storage=acme.json"
      - "--certificatesResolvers.cloudflare.acme.email=yourcfemail@here.com"
      - "--certificatesResolvers.cloudflare.acme.dnsChallenge=true"
      - "--certificatesResolvers.cloudflare.acme.dnschallenge.provider=cloudflare"
      - "--certificatesResolvers.cloudflare.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53"
      - "--serversTransport.insecureSkipVerify=true" # Or proxmox gives an error 500 due to its own self-signed cert

    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/acme.json:/acme.json

networks:
  proxy:
    external: true

Of particular note here is that both configurations explicitly define which network they are using, in this case "proxy", but it can be named anything. It just has to be the same across all apps for which Traefik is serving as a proxy. These are marked as external because the proxy network was manually created by running: docker network create proxy but this might be unnecessary depending on HOW exactly you are running Traefik. For example, if running Traefik with Portainer, you can follow their official docs on how to set up Traefik and Portainer together, and you can just focus on the Homarr docker labels instead.

Securing Homarr with Caddy

If you are using Caddy as the reverse proxy for your setup, your docker-compose.yml should look something like this:

services:
  # <-- [Homarr installation]

  caddy:
    container_name: caddy
    image: caddy:2
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 443:443/udp
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - proxy

volumes:
  caddy_data:
  caddy_config:

networks:
  proxy:
    external: true

Important

Homarr and Caddy must share the same Docker network:

homarr:
  ...
  networks:
    - proxy

Next, create a file named Caddyfile at the same level as your docker-compose.yml file with the following content:

homarr.mydomain.com {
	reverse_proxy homarr:7575
}

If you want more information about working with Caddy and Docker Compose, refer to the official documentation: https://caddyserver.com/docs/running#docker-compose

On this page