---
title: 01.11 The Base URL, Custom Domains, and Reverse Proxies
description: How SimpleRisk resolves its Base URL, and how to serve an instance on a hostname it doesn't naturally see — CNAME/DNS, a Host-preserving reverse proxy,…
---

[Skip to content](https://support.simplerisk.com/kb/01-11-base-url-and-reverse-proxies#main-content)

English

Show submenu for translations

[Customer portal](https://support.simplerisk.com/tickets?hsLang=en)

[![SimpleRisk logo of a man walking a tight rope](https://support.simplerisk.com/hs-fs/hubfs/simplerisk_logo_long_small-4.png?width=377&height=72&name=simplerisk_logo_long_small-4.png)](https://www.simplerisk.com/)

Open main navigation

Close main navigation

- English
  
  Show submenu for translations
- [Customer portal](https://support.simplerisk.com/tickets)
- [Contact us](https://www.simplerisk.com/about-us/contact-us)

[Contact us](https://www.simplerisk.com/about-us/contact-us)

 How can we help you?

- There are no suggestions because the search field is empty.

1. [SimpleRisk Knowledge Base](https://support.simplerisk.com/kb?hsLang=en)
2. [Administrator Guide](https://support.simplerisk.com/kb/administrator-guide?hsLang=en)
3. [01 Installation and Deployment](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#01-installation-and-deployment)

# 01.11 The Base URL, Custom Domains, and Reverse Proxies

## How SimpleRisk resolves its Base URL, and how to serve an instance on a hostname it doesn't naturally see — CNAME/DNS, a Host-preserving reverse proxy, the HTTPS signal SimpleRisk actually reads, and pinning the simplerisk\_base\_url setting so every generated link, SAML round-trip, and security check uses the public domain.

## Why this matters

Every absolute URL SimpleRisk emits comes from one setting: the Base URL. The password-reset link in an email, the SAML entity ID and Assertion Consumer Service (ACS) URL an identity provider validates against, the server URL the in-product Swagger console pre-fills, the questionnaire share link an external respondent clicks — all of them are built by gluing a path onto the Base URL. Get it wrong and the application still loads, but the links it hands out point somewhere useless.

The Base URL is also a security control, not a cosmetic label. It feeds the CSRF referer check, the Content-Security-Policy allowlist, and the password-reset flow's host-match guard, which refuses to send a reset email if the Base URL's host doesn't match the host the request arrived on. This is why the field lives on the **Security** tile rather than with the display preferences.

For most installs none of this needs a second thought. You run the installer at `https://simplerisk.example.com`, the Base URL is captured as exactly that, and it's correct forever. This article exists for the case where the hostname *users* type isn't the hostname the *instance* naturally sees — a customer who wants their GRC program at `grc.customer.com` while the instance itself answers to an internal name, a load-balancer DNS entry, or a per-tenant hosting hostname. That's a CNAME plus a reverse proxy plus a deliberately-pinned Base URL, and the three have to agree or things break in ways that are annoying to diagnose after the fact.

## How SimpleRisk resolves the Base URL

Before the setup steps, it's worth understanding exactly where the value comes from, because the mechanics explain every pitfall later in the article.

**The resolution order is: stored setting → session → auto-derive from the request.** SimpleRisk first checks whether the Base URL is already stored. If it isn't, it falls back to the value captured in the current session (written at login, SSO grant, and API authentication). If that's also unset, it derives a value from the current request — the scheme, the hostname, the port, and the subdirectory the application is installed under — and writes that derived value back into storage. The first request that resolves the Base URL this way freezes it; the value doesn't recompute on every request.

Two things about that derivation drive most of what follows. First, **the scheme is decided solely by the request's own HTTPS signal.** SimpleRisk does not read `X-Forwarded-Proto`, `X-Forwarded-Host`, or any other proxy header when deciding http-versus-https or which hostname it lives on — it looks only at whether the request that reached it was itself served over TLS. Second, whatever gets auto-derived is what gets stored; there's no separate reconciliation step later.

**The Security-tile setter is intentionally restrictive, and this surprises people.** Saving the field there runs two checks. The first is a plain format check — protocol required, then hostname, optional port, optional path, no query string or fragment. The second is the one that trips admins up: the value you typed must *exactly equal* the URL SimpleRisk derives from the current request. In other words, you can only save a Base URL that matches the hostname you're browsing on right now. Type anything else and the save is rejected with an **Invalid SimpleRisk Base URL!** message. That constraint is the single most important thing to internalize for the custom-domain case: to set the Base URL to `https://grc.customer.com` through the UI, you have to be reaching the instance *as* `grc.customer.com` at that moment.

What reads the Base URL, so you know what's at stake if it's wrong:

- **Security controls.** The CSRF referer/origin gate, the Content-Security-Policy header's allowlisted domains, the password-reset host-header anti-tamper check, and the safe-redirect logic behind every `Location:` header after a permission denial or login.
- **Off-server links.** Password-reset and notification emails, license-expiry emails, the SAML SP entity ID / ACS / Single Logout URLs, the Swagger UI server field, and questionnaire share links.

One clarification that saves a support round-trip: **SimpleRisk's SSO is SAML-only.** There is no OIDC/OpenID Connect login and therefore no OIDC redirect-URI to register — if someone asks where to point the OIDC callback, the answer is that it doesn't exist here.

## Before you start

Have these in hand:

- **The two hostnames, named clearly.** The *public* hostname is what users and identity providers will use (`{grc.customer.com}`). The *natural* hostname is whatever the instance answers to today — an EC2 or container name, a load-balancer DNS record, or a per-tenant hosting hostname. Write both down; you'll reference them in every step.
- **DNS control for the public domain** — enough to add a CNAME (or A/ALIAS) record.
- **Control of the reverse proxy or load balancer** that will sit in front of the instance, plus a TLS certificate for the public hostname. TLS termination itself is covered in [HTTPS and TLS Configuration](https://support.simplerisk.com/kb/01-07-https-and-tls-configuration?hsLang=en); this article assumes you've read it and focuses on the hostname-mismatch parts.
- **SimpleRisk admin access** to set the Base URL through the UI, *or* direct database access if you'll set it in SQL.
- **A maintenance window if SAML SSO is in play.** Changing the Base URL changes the SP entity ID and ACS URL, and the identity provider has to be updated to match or logins break. Plan the IdP-side update into the same change.

## Step-by-step

### 1. Point DNS at the instance

Create the DNS record for the public hostname. A CNAME pointing the public name at the natural name is the common pattern:

```
grc.customer.com.   CNAME   {natural-host}.
```

If the front door is a load balancer or a proxy with its own address, use an A / AAAA / ALIAS record to that address instead. The mechanism doesn't matter to SimpleRisk; what matters is that a browser resolving `{grc.customer.com}` ends up at the proxy that fronts the instance.

SimpleRisk's Health Check will later resolve whatever hostname it sees, so a public hostname that doesn't resolve shows up as a failed check rather than a silent mystery. Give DNS time to propagate before you rely on it.

### 2. Terminate TLS for the public hostname

Obtain and install a certificate for `{grc.customer.com}` at the proxy or load balancer, and configure the HTTP-to-HTTPS redirect there. Follow [HTTPS and TLS Configuration](https://support.simplerisk.com/kb/01-07-https-and-tls-configuration?hsLang=en) for the certificate mechanics. The certificate has to cover the *public* hostname, because that's the name in the browser's address bar — a certificate for the natural hostname produces a name-mismatch warning.

### 3. Configure the reverse proxy to preserve the public Host and scheme

This is the step that makes or breaks the deployment, and it comes down to two rules.

**Rule one: forward the public Host header unchanged.** SimpleRisk derives its hostname from the incoming request's `Host` header. If the proxy rewrites `Host` to the backend's internal name, the instance thinks it lives at the internal name, and three separate things go wrong: the auto-derived Base URL is wrong, the password-reset anti-tamper check refuses to send, and the Health Check's Base-URL-match test fails. Preserve the original `Host`.

**Rule two: tell the backend the request was HTTPS.** SimpleRisk reads the scheme only from the request's own HTTPS signal; it never looks at `X-Forwarded-Proto`. So a proxy that terminates TLS and then speaks plain HTTP to the backend leaves the application convinced it's running on `http://`. Forwarding `X-Forwarded-Proto: https` is good hygiene, but on its own it does nothing here. You have to translate that signal into an HTTPS flag at the backend's web server, or pin the Base URL in step 4. Do both for a proxied HTTPS deployment: the backend HTTPS flag is also what sets the `Secure` attribute on the session cookie.

**Nginx reverse proxy** in front of a SimpleRisk backend:

```
server {
    listen 443 ssl;
    server_name grc.customer.com;

    ssl_certificate     /etc/letsencrypt/live/grc.customer.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grc.customer.com/privkey.pem;

    location / {
        proxy_pass         http://{simplerisk-backend};
        proxy_set_header   Host              $host;   # preserve grc.customer.com — do NOT hardcode the backend name
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme; # informational; SimpleRisk does not read this
    }
}
```

On the **backend**, translate the scheme so the web server reports the request as HTTPS. For an Apache backend, add to the vhost:

```
SetEnvIf X-Forwarded-Proto "https" HTTPS=on
```

For an Nginx + PHP-FPM backend, set the FastCGI parameter when the forwarded scheme is https:

```
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
    fastcgi_param HTTPS on;   # gate on $http_x_forwarded_proto = https if the backend also serves plain HTTP
}
```

**Apache as the reverse proxy** (rather than Nginx) uses `ProxyPreserveHost On` to get rule one for free:

```

    ServerName grc.customer.com SSLEngine on SSLCertificateFile /etc/letsencrypt/live/grc.customer.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/grc.customer.com/privkey.pem ProxyPreserveHost On ProxyPass / http://{simplerisk-backend}/ ProxyPassReverse / http://{simplerisk-backend}/ RequestHeader set X-Forwarded-Proto "https" 
  
```

If TLS terminates at a cloud load balancer (AWS ALB, Azure Application Gateway, GCP Load Balancer), the equivalents are: leave host-header preservation on (ALB preserves `Host` by default), and either configure the backend web server to honor the forwarded proto as above or rely on the pinned Base URL from step 4. Cloud LBs can't set an HTTPS flag inside the backend for you, so the pinned Base URL is usually the cleaner path in that topology.

### 4. Pin the Base URL to the public hostname

With the proxy forwarding the public `Host` and scheme, set the Base URL to the public URL. Two ways, depending on whether you can already reach the instance as the public hostname.

**Option A — through the UI (preferred once the proxy works).** Browse to the instance *via the public hostname* (`https://{grc.customer.com}`), then:

1. **Settings** cog (top-right of any page) → **Settings Hub** → **Security** tile.
2. Find the **SimpleRisk Base URL** field.
3. Enter `https://{grc.customer.com}` (include the subdirectory if this is a subpath install, e.g. `https://{grc.customer.com}/simplerisk`).
4. Save.

This only succeeds when you're actually reaching the instance as the public hostname, because of the exact-match validation described earlier. If you get **Invalid SimpleRisk Base URL!**, you're almost certainly still browsing via the natural hostname, or the proxy is rewriting `Host` — revisit step 3 before assuming the field is broken.

**Option B — directly in the database.** For a bootstrap where you can't yet browse via the public name, or for automation across a fleet, write the setting in SQL:

```
UPDATE settings SET value = 'https://grc.customer.com' WHERE name = 'simplerisk_base_url';
```

Store it with no trailing slash; the application trims one anyway, but keeping the stored value clean avoids confusion. The database path bypasses the exact-match validation entirely — which is exactly why it's the escape hatch for the chicken-and-egg case, and also why you should double-check the value. After a direct database change, log out and back in so the session's cached copy is refreshed to the new value, and clear PHP opcache if your deployment caches aggressively.

### 5. Verify end to end

Don't trust the setting until you've watched it produce a correct link.

- **Health Check.** Settings cog → Settings Hub → System → **Health Check**. The Base-URL check reports whether the stored value matches the hostname you're connected on, and the DNS check confirms the detected server name resolves. Both should pass when you're on the public hostname.
- **A real email link.** Trigger a password reset for a test user, or fire a notification, and confirm the link in the email uses `https://{grc.customer.com}` and not the natural hostname or `http://`. This is the fastest way to catch a scheme or host mistake, because email links are pure Base URL.
- **SAML, if configured.** Re-download the SP metadata (Authentication Extra → SAML tab → **Download SP Metadata**) and confirm the entity ID and ACS URL now carry the public hostname. Update the identity provider with the new metadata. Until the IdP is updated, SSO logins will fail the audience/ACS check.
- **A quick `curl`.** `curl -I https://grc.customer.com/` should return a normal SimpleRisk response over TLS with no redirect to the natural hostname.

## Common pitfalls

- **The proxy rewrites the Host header to the backend name.** The most common cause of "the app is up but every link is wrong." The instance derives its identity from the incoming Host header, so a rewritten one poisons the auto-derived Base URL, and, more sharply, the password-reset flow silently refuses to send because the Base URL's host no longer matches the request's host. Preserve the original `Host` (`proxy_set_header Host $host` in Nginx, `ProxyPreserveHost On` in Apache).
- **TLS terminates at the proxy and the backend only sees HTTP.** SimpleRisk decides http-versus-https from the request's own signal alone. Without a translation step the app derives `http://` links, and the session cookie loses its `Secure` attribute. Set the backend's HTTPS flag (or pin the Base URL, or both). Forwarding `X-Forwarded-Proto` by itself changes nothing — the application never reads it.
- **Trying to save the custom domain while browsing the natural one.** The UI validation requires the typed URL to match the current request's hostname. You cannot set `https://grc.customer.com` from a session you opened at the natural hostname — you'll get **Invalid SimpleRisk Base URL!**. Either reach the instance through the public hostname first, or use the database option.
- **An empty or malformed Base URL quietly weakens the CSP.** When the value doesn't pass URL validation, the Content-Security-Policy header falls back to a wide-open policy instead of the scoped allowlist. The application keeps working, so nothing screams — but you've lost a layer of XSS defense until the value is fixed. Verify the setting is a well-formed absolute URL after any direct database edit.
- **Changing the Base URL and forgetting the identity provider.** SAML entity IDs, ACS URLs, and SLO URLs are all built from the Base URL. Move the hostname without re-sharing SP metadata and every SSO login fails at the IdP. Treat the Base URL change and the IdP update as one change with one maintenance window.
- **Subpath installs lose their path segment.** If SimpleRisk lives under a subdirectory (`https://{host}/simplerisk`), the Base URL must include that segment. Setting it to the bare host breaks asset and link paths. The auto-derivation preserves the subdirectory; a hand-set value has to include it too.
- **Assuming the value recomputes.** The first request to resolve the Base URL writes it back into storage, so a stale value doesn't self-heal when you move the instance. After any hostname change (a migration, a cutover, a new custom domain), set the Base URL explicitly rather than waiting for it to notice.
- **Multiple public hostnames, one Base URL.** SimpleRisk stores a single Base URL. If an instance is reachable under several names (a vanity domain and the natural name, or split-horizon DNS), pick the canonical public one and expect generated links and SAML to use only that. Requests arriving on the other names still work, but their emails and SSO round-trips point at the canonical name.

## Related

- [HTTPS and TLS Configuration](https://support.simplerisk.com/kb/01-07-https-and-tls-configuration?hsLang=en)
- [The Initial Configuration Wizard](https://support.simplerisk.com/kb/01-05-the-initial-configuration-wizard?hsLang=en)
- [Installing via Docker](https://support.simplerisk.com/kb/01-03-installing-via-docker?hsLang=en)
- [Securing the Web Server](https://support.simplerisk.com/kb/09-05-securing-the-web-server?hsLang=en)
- [Configuring SAML SSO](https://support.simplerisk.com/kb/04-04-configuring-saml-sso?hsLang=en)
- [Settings Reference](https://support.simplerisk.com/kb/13-01-config-settings-reference?hsLang=en)

- [FAQs](https://support.simplerisk.com/kb/faqs?hsLang=en)
- [SimpleRisk Extras](https://support.simplerisk.com/kb/simplerisk-extras?hsLang=en#main-content)

    - [Vulnerability Management Extra](https://support.simplerisk.com/kb/simplerisk-extras?hsLang=en#vulnerability-management-extra)
    - [Team Separation Extra](https://support.simplerisk.com/kb/simplerisk-extras?hsLang=en#team-separation-extra)
    - [Import-Export Extra](https://support.simplerisk.com/kb/simplerisk-extras?hsLang=en#import-export-extra)
- [Administrator Guide](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#main-content)

    - [00 About This Guide](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#00-about-this-guide)
    - [01 Installation and Deployment](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#01-installation-and-deployment)
    - [02 Upgrades and Maintenance](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#02-upgrades-and-maintenance)
    - [03 Users and Permissions](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#03-users-and-permissions)
    - [04 Authentication](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#04-authentication)
    - [05 Customization](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#05-customization)
    - [06 Configuring Risk and Compliance](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#06-configuring-risk-and-compliance)
    - [07 Integrations](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#07-integrations)
    - [08 The API](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#08-the-api)
    - [09 Encryption and Data Security](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#09-encryption-and-data-security)
    - [10 Workflows and Automation](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#10-workflows-and-automation)
    - [11 Reporting and Auditing](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#11-reporting-and-auditing)
    - [12 Operations](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#12-operations)
    - [13 Reference](https://support.simplerisk.com/kb/administrator-guide?hsLang=en#13-reference)
- [User Guide](https://support.simplerisk.com/kb/user-guide?hsLang=en#main-content)

    - [00 Foundations of GRC](https://support.simplerisk.com/kb/user-guide?hsLang=en#00-foundations-of-grc)
    - [01 Risk Management](https://support.simplerisk.com/kb/user-guide?hsLang=en#01-risk-management)
    - [02 Compliance Management](https://support.simplerisk.com/kb/user-guide?hsLang=en#02-compliance-management)
    - [03 Governance](https://support.simplerisk.com/kb/user-guide?hsLang=en#03-governance)
    - [04 Asset and Data Inventory](https://support.simplerisk.com/kb/user-guide?hsLang=en#04-asset-and-data-inventory)
    - [05 Threat and Vulnerability Management](https://support.simplerisk.com/kb/user-guide?hsLang=en#05-threat-and-vulnerability-management)
    - [06 Assessments](https://support.simplerisk.com/kb/user-guide?hsLang=en#06-assessments)
    - [07 Incident Management](https://support.simplerisk.com/kb/user-guide?hsLang=en#07-incident-management)
    - [08 Audit and Reporting](https://support.simplerisk.com/kb/user-guide?hsLang=en#08-audit-and-reporting)
    - [09 Day-to-Day SimpleRisk](https://support.simplerisk.com/kb/user-guide?hsLang=en#09-day-to-day-simplerisk)
    - [10 Continuous Improvement](https://support.simplerisk.com/kb/user-guide?hsLang=en#10-continuous-improvement)
- [SimpleRisk User Guides](https://support.simplerisk.com/kb/simplerisk-user-guides?hsLang=en)
- [Troubleshooting](https://support.simplerisk.com/kb/troubleshooting?hsLang=en)
- [SimpleRisk Hosted](https://support.simplerisk.com/kb/simplerisk-hosted?hsLang=en)
- [How To videos](https://support.simplerisk.com/kb/how-to-videos?hsLang=en)

[![favicon-1](https://support.simplerisk.com/hs-fs/hubfs/favicon-1.png?width=35&height=35&name=favicon-1.png "favicon-1")](https://www.simplerisk.com)

<https://www.facebook.com/simplerisk/> <https://www.twitter.com/simpleriskfree/> <https://www.linkedin.com/company/simplerisk/>

Copyright © 2026, SimpleRisk, Inc.