Introduction

When migrating Ansible Tower or AWX to a new domain (e.g., from tower.oldcorp.com to tower.newcorp.com), the reverse proxy configuration may retain references to the old canonical hostname. This causes HTTP redirects to the old domain, broken API client connections, and certificate validation failures. The issue often persists even after updating the primary Tower configuration because the canonical host setting is cached in multiple places: nginx configuration, Django settings, HAProxy, load balancers, and Tower's internal database.

This typically occurs during: - Corporate rebranding or domain changes - Migrating from on-premises to cloud-hosted Tower - Setting up Tower in a new network segment - Changing from HTTP to HTTPS with a new domain

Symptoms

API clients receive redirects to the old domain:

```bash $ curl -L -k https://tower.newcorp.com/api/v2/ping/ HTTP 301 Moved Permanently Location: https://tower.oldcorp.com/api/v2/ping/

curl: (6) Could not resolve host: tower.oldcorp.com ```

Browser access shows certificate errors:

``` Your connection is not private NET::ERR_CERT_COMMON_NAME_INVALID

This certificate is for: tower.oldcorp.com You are trying to access: tower.newcorp.com ```

In nginx access logs:

bash
# /var/log/nginx/access.log
10.0.1.100 - - [15/Mar/2024:14:23:45 +0000] "GET /api/v2/ping/ HTTP/1.1" 301 178 "-" "curl/7.68.0"
10.0.1.101 - - [15/Mar/2024:14:24:12 +0000] "GET / HTTP/1.1" 302 145 "https://tower.newcorp.com/" "Mozilla/5.0"

Tower API returns incorrect URLs in responses:

json
{
  "id": 1,
  "type": "job_template",
  "url": "/api/v2/job_templates/1/",
  "related": {
    "jobs": "/api/v2/job_templates/1/jobs/",
    "labels": "/api/v2/job_templates/1/labels/"
  },
  "name": "Deploy Application",
  "webhook_credential": null,
  "webhook_key": "https://tower.oldcorp.com/api/v2/job_templates/1/webhook_key/"
}

In Tower callback receiver logs:

bash
2024-03-15 14:23:45,123 ERROR    awx.main.callbacks Callback receiver failed: HTTPSConnectionPool(host='tower.oldcorp.com', port=443)
2024-03-15 14:24:12,456 WARNING  awx.main.dispatch Job event submission failed: Name or service not known

Ansible CLI clients fail:

bash
$ tower-cli job launch --job-template=1
Error: Failed to establish connection to https://tower.oldcorp.com/
Please verify TOWER_HOST is set correctly

Common Causes

The old canonical host persists due to configuration being stored in multiple layers:

  1. 1.nginx server_name directive: The nginx configuration explicitly lists server_name tower.oldcorp.com, causing it to respond only to that hostname and redirect others.
  2. 2.Tower's TOWER_URL setting: Tower stores its base URL in settings.py and uses it to generate absolute URLs in API responses, webhook URLs, and callback endpoints.
  3. 3.Database-stored URLs: Some Tower objects store absolute URLs in the database during creation (webhook keys, callback URLs, notification templates).
  4. 4.Load balancer or HAProxy health checks: Health checks may be configured against the old hostname, causing failures after the cutover.
  5. 5.SSL certificate SAN entries: The SSL certificate may only include the old domain in its Subject Alternative Names, causing browser warnings.
  6. 6.Cache and browser history: Browsers and HTTP caches may have cached redirects or HSTS settings for the old domain.
  7. 7.Instance hostname in Tower database: Tower stores each instance's hostname in the database, which may still reference the old domain.

Step-by-Step Fix

Step 1: Identify All References to Old Domain

Find all configuration files containing the old hostname:

```bash OLD_DOMAIN="tower.oldcorp.com" NEW_DOMAIN="tower.newcorp.com"

# Search Tower configuration grep -r "$OLD_DOMAIN" /etc/tower/ grep -r "$OLD_DOMAIN" /etc/nginx/ grep -r "$OLD_DOMAIN" /etc/haproxy/

# Search Tower database for absolute URLs sudo -u postgres psql -d awx -c " SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%url%' OR column_name LIKE '%host%';"

# Find URLs in database sudo -u postgres psql -d awx -c " SELECT 'main_jobtemplate' as tbl, id, webhook_key FROM main_jobtemplate WHERE webhook_key LIKE '%$OLD_DOMAIN%';"

# Check nginx configuration nginx -T 2>/dev/null | grep -i "server_name"

# Check Tower settings grep -E "TOWER_URL|SITE_URL|ALLOWED_HOSTS" /etc/tower/settings.py ```

Step 2: Update Tower Configuration

Update the primary Tower settings:

```bash # Backup current configuration sudo cp /etc/tower/settings.py /etc/tower/settings.py.bak

# Update settings sudo sed -i "s/$OLD_DOMAIN/$NEW_DOMAIN/g" /etc/tower/settings.py

# Or manually edit sudo vim /etc/tower/settings.py

# Update these settings: TOWER_URL = 'https://tower.newcorp.com' SITE_NAME = 'tower.newcorp.com'

ALLOWED_HOSTS = [ 'localhost', '127.0.0.1', 'tower.newcorp.com', 'tower01.newcorp.com', 'tower02.newcorp.com', ]

# CSRF and cookie settings CSRF_TRUSTED_ORIGINS = ['https://tower.newcorp.com'] SESSION_COOKIE_DOMAIN = '.newcorp.com' # For SSO across subdomains

# Webhook base URL AWX_WEBHOOK_BASE_URL = 'https://tower.newcorp.com/api/v2/webhook/' ```

Step 3: Update nginx Configuration

Fix the nginx virtual host configuration:

```bash # Edit nginx configuration sudo vim /etc/nginx/nginx.conf

# Or edit the Tower-specific config sudo vim /etc/nginx/conf.d/tower.conf

# Update server_name directive server { listen 80; listen 443 ssl; server_name tower.newcorp.com tower01.newcorp.com tower02.newcorp.com;

# Update SSL certificate (if using new cert) ssl_certificate /etc/ssl/certs/tower.newcorp.com.crt; ssl_certificate_key /etc/ssl/private/tower.newcorp.com.key;

# Remove or update any hardcoded redirect # return 301 https://tower.oldcorp.com$request_uri; # REMOVE THIS

# Update proxy headers if needed location / { proxy_set_header Host $host; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; } }

# Test nginx configuration sudo nginx -t

# Reload nginx sudo systemctl reload nginx ```

Step 4: Update Database-Stored URLs

Update URLs stored in Tower's database:

```bash # Connect to Tower database sudo -u postgres psql -d awx

# Start a transaction BEGIN;

-- Update webhook URLs in job templates UPDATE main_jobtemplate SET webhook_key = REPLACE(webhook_key, 'tower.oldcorp.com', 'tower.newcorp.com') WHERE webhook_key LIKE '%tower.oldcorp.com%';

-- Update callback URLs UPDATE main_job SET job_events = REPLACE(job_events::text, 'tower.oldcorp.com', 'tower.newcorp.com')::json WHERE job_events::text LIKE '%tower.oldcorp.com%';

-- Update notification templates UPDATE main_notificationtemplate SET notification_configuration = REPLACE(notification_configuration::text, 'tower.oldcorp.com', 'tower.newcorp.com')::json WHERE notification_configuration::text LIKE '%tower.oldcorp.com%';

-- Update instance hostnames UPDATE main_instance SET hostname = REPLACE(hostname, 'tower.oldcorp.com', 'tower.newcorp.com') WHERE hostname LIKE '%tower.oldcorp.com%';

-- Verify changes SELECT hostname FROM main_instance; SELECT webhook_key FROM main_jobtemplate WHERE webhook_key IS NOT NULL LIMIT 5;

-- Commit if correct COMMIT; -- Or rollback if issues -- ROLLBACK; ```

Step 5: Update SSL Certificate

Install the correct certificate for the new domain:

```bash # Generate new CSR for new domain openssl req -new -newkey rsa:2048 -nodes \ -keyout /etc/ssl/private/tower.newcorp.com.key \ -out /etc/ssl/certs/tower.newcorp.com.csr \ -subj "/C=US/ST=State/L=City/O=Organization/CN=tower.newcorp.com" \ -addext "subjectAltName = DNS:tower.newcorp.com,DNS:tower01.newcorp.com,DNS:tower02.newcorp.com"

# Submit CSR to your CA # After receiving the signed certificate, install it sudo cp tower.newcorp.com.crt /etc/ssl/certs/ sudo cp tower.newcorp.com.key /etc/ssl/private/

# Set proper permissions sudo chmod 600 /etc/ssl/private/tower.newcorp.com.key sudo chmod 644 /etc/ssl/certs/tower.newcorp.com.crt

# Update certificate symlinks if Tower uses them sudo ln -sf /etc/ssl/certs/tower.newcorp.com.crt /etc/tower/tower.cert sudo ln -sf /etc/ssl/private/tower.newcorp.com.key /etc/tower/tower.key

# Verify certificate openssl x509 -in /etc/ssl/certs/tower.newcorp.com.crt -text -noout | grep -A1 "Subject Alternative Name" ```

Step 6: Update Load Balancer Configuration

If using an external load balancer:

```bash # For HAProxy sudo vim /etc/haproxy/haproxy.cfg

frontend tower_https bind *:443 ssl crt /etc/ssl/tower.newcorp.com.pem acl is_tower hdr(host) -i tower.newcorp.com tower01.newcorp.com redirect prefix https://tower.newcorp.com code 301 if !is_tower default_backend tower_servers

backend tower_servers balance roundrobin option httpchk HEAD /api/v2/ping/ HTTP/1.1\r\nHost:\ tower.newcorp.com server tower01 10.0.1.10:443 ssl check verify none server tower02 10.0.1.11:443 ssl check verify none

sudo systemctl reload haproxy

# For AWS ALB aws elbv2 modify-listener \ --listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/tower-alb/xxx/yyy \ --certificates CertificateArn=arn:aws:acm:region:account:certificate/zzz ```

Step 7: Clear Caches and Restart

Clear all caches and restart services:

```bash # Clear Tower cache redis-cli FLUSHDB

# Clear Django cache sudo -u awx awx-manage clear_cache

# Clear static file cache sudo rm -rf /var/lib/awx/public/static/.cache

# Restart Tower services sudo ansible-tower-service restart

# Verify services are running sudo ansible-tower-service status

# Test direct access curl -k https://localhost/api/v2/ping/ ```

Step 8: Update DNS and Verify

Ensure DNS points to the new infrastructure:

```bash # Check DNS resolution dig tower.newcorp.com +short dig tower.oldcorp.com +short # Should not resolve or redirect

# Test from external client curl -L https://tower.newcorp.com/api/v2/ping/ | jq

# Verify no redirects to old domain curl -I https://tower.newcorp.com/ 2>&1 | grep -i location # Should show no redirect or only internal paths ```

Verification

Confirm all components use the new domain:

```bash # Test API returns correct URLs curl -k -u admin:password https://tower.newcorp.com/api/v2/job_templates/1/ | jq '.webhook_key' # Should show: "https://tower.newcorp.com/api/v2/job_templates/1/webhook_key/"

# Test webhook triggers work curl -X POST https://tower.newcorp.com/api/v2/webhook/job_template:1:abc123/

# Verify Tower ping returns correct hostname curl -k https://tower.newcorp.com/api/v2/ping/ | jq '.active_node'

# Launch a test job and verify callbacks work curl -X POST -k -u admin:password https://tower.newcorp.com/api/v2/job_templates/1/launch/ | jq '.job'

# Check job events are being received # (Look for job events in Tower UI or API)

# Verify browser access with no certificate warnings # Open https://tower.newcorp.com/ in browser ```

  • [ansible-tower-ssl-certificate-errors](/articles/ansible-tower-ssl-certificate-errors)
  • [ansible-webhook-failures-after-url-change](/articles/ansible-webhook-failures-after-url-change)
  • [ansible-behind-load-balancer-misconfiguration](/articles/ansible-behind-load-balancer-misconfiguration)
  • [WordPress troubleshooting: Ansible Artifact Download Uses an Old Mi](ansible-artifact-download-uses-an-old-mirror-after-proxy-change)
  • [WordPress troubleshooting: Ansible Audit Trail Misses Events Under ](ansible-audit-trail-misses-events-under-burst-load)
  • [WordPress troubleshooting: Ansible Background Worker Gets Stuck in ](ansible-background-worker-stuck-in-a-retry-loop)
  • [WordPress troubleshooting: Ansible Backup Completes but Restore Fai](ansible-backup-completes-but-restore-fails-checksum-validation)
  • [WordPress troubleshooting: Ansible Batch Importer Duplicates Rows A](ansible-batch-importer-duplicates-rows-after-a-retry)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "WordPress troubleshooting: Ansible Reverse Proxy Keeps an Old Canon", "description": "Learn how to fix Ansible Reverse Proxy Keeps an Old Canonical Host After Domain Cutover. Professional WordPress troubleshooting solutions with step-by-step guidance. WP error fix, WordPress optimization, WP security, WordPress performance.", "url": "https://www.fixwikihub.com/ansible-reverse-proxy-keeps-an-old-canonical-host-after-domain-cutover", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-03-07T17:01:07.421Z", "dateModified": "2026-03-07T17:01:07.421Z" } </script>