Reverse Proxy Canva Sites with NGINX With Custom Domain and Remove Canva Watermark Footer

Canva is a popular design tool that allows users to create professional-looking websites without coding knowledge. However, these sites are hosted on Canva’s domain (e.g., username.my.canva.site
). If you want to use your own domain while leveraging Canva’s website builder, you’ll need to set up a reverse proxy with NGINX. This article explores how to effectively reverse engineer a Canva site using NGINX, allowing you to serve Canva-created content from your own domain.
Understanding the Challenge
When reverse proxying a Canva site, several challenges arise:
- Asset path rewriting: Canva stores assets (images, CSS, JS) in the
_assets/
directory, which needs to be remapped - Cross-Origin Resource Sharing (CORS): Assets loaded from your domain need proper CORS headers
- SSL/TLS configuration: Ensuring secure connections between your server, users, and Canva
- Content modification: Removing Canva branding or customizing the site appearance
- Path handling: Managing different site paths correctly
Prerequisites
Before starting, you’ll need:
- A registered domain name
- A server with NGINX installed
- SSL certificates for your domain (Let’s Encrypt works well)
- Basic understanding of NGINX configuration
Step 1: Basic NGINX Server Configuration
Start by setting up the basic server block for your domain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
server { listen 80; listen 443 ssl; server_name yourdomain.com www.yourdomain.com; # SSL certificate configuration ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Root directory for your server root /var/www/html; index index.html; # Logs access_log /var/log/nginx/yourdomain.com.access.log; error_log /var/log/nginx/yourdomain.com.error.log; } |
Step 2: Setting Up the Main Proxy Configuration
Now, we’ll add the proxy configuration to forward requests to Canva:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
location / { proxy_pass https://username.my.canva.site/your-site/; proxy_set_header Host username.my.canva.site; 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; proxy_redirect off; proxy_ssl_server_name on; proxy_ssl_verify off; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; } |
This configuration forwards all requests from your domain to the Canva site. However, it’s not enough to handle assets and content properly.
Step 3: Content Transformation with sub_filter
A key challenge is handling Canva’s asset paths. We need to modify the HTML, CSS, and JavaScript to reference assets through our domain. This is where sub_filter
comes in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
location / { # Previous proxy configuration remains # Prevent Canva from sending compressed responses (so NGINX can modify them) proxy_set_header Accept-Encoding ""; # Apply `sub_filter` to all content types that may contain `_assets/` sub_filter_types text/html text/css text/javascript application/javascript application/json; # Replace `_assets/` URLs to serve from `/assets/` sub_filter 'href="_assets/' 'href="/assets/'; sub_filter 'src="_assets/' 'src="/assets/'; sub_filter 'url("_assets/' 'url("/assets/'; sub_filter 'content="https://username.my.canva.site/your-site/_assets/' 'content="https://yourdomain.com/assets/'; # Remove Canva footer sub_filter '</head>' '<style>.footer-overflow-container { display: none !important; }</style></head>'; sub_filter_once off; # Ensure `sub_filter` works by disabling buffering proxy_buffering off; proxy_http_version 1.1; # Re-enable compression after modification gzip on; gzip_types text/html text/css text/javascript application/javascript; gzip_vary on; } |
This configuration does several important things:
- Disables incoming compression so NGINX can modify the content
- Specifies which content types should be processed with
sub_filter
- Replaces all references to
_assets/
with/assets/
- Adds CSS to hide the Canva footer
- Ensures all instances are replaced (not just the first one)
- Disables buffering to make string replacement work properly
- Re-enables compression for the modified content
Step 4: Creating an Assets Proxy Handler
Now we need to handle requests to the /assets/
path and proxy them to Canva’s _assets/
directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
location /assets/ { proxy_pass https://username.my.canva.site/your-site/_assets/; proxy_set_header Host username.my.canva.site; 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; proxy_redirect off; proxy_ssl_server_name on; proxy_ssl_verify off; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; # Add CORS Headers proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept"; # Prevent Canva from caching restrictions proxy_hide_header Vary; # Handle preflight (OPTIONS) requests if ($request_method = 'OPTIONS') { return 204; } # Enable asset caching expires 30d; access_log off; } |
This configuration:
- Forwards requests for
/assets/
to Canva’s_assets/
path - Sets up CORS headers to allow cross-origin resource loading
- Handles preflight requests for CORS
- Sets aggressive caching for static assets (30 days)
- Disables access logging for assets to reduce log volume
Step 5: Handling Multiple Paths (Optional)
If your Canva site has different sections or if you want to proxy multiple Canva sites under different paths, you can add additional location blocks:
1 2 3 4 5 6 7 |
location /section-name/ { proxy_pass https://username.my.canva.site/section-path/; # Include the same sub_filter and proxy configurations as the root location # ... } |
Step 6: DNS and Cloudflare Configuration (Optional)
If you’re using Cloudflare for DNS and proxy services, you can add this to your NGINX configuration:
1 2 3 4 5 6 |
# For Cloudflare real_ip_header CF-Connecting-IP; # DNS resolvers resolver 1.1.1.1 8.8.8.8 valid=300s ipv6=off; |
This ensures:
- NGINX gets the real client IP from Cloudflare
- DNS resolution works properly
Complete Configuration Example
Here’s a complete configuration example combining all the elements:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
server { listen 80; listen 443 ssl; server_name yourdomain.com www.yourdomain.com; # SSL configuration ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; real_ip_header CF-Connecting-IP; # Main proxy for the root path location / { proxy_pass https://username.my.canva.site/your-site/; proxy_set_header Host username.my.canva.site; 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; proxy_redirect off; proxy_ssl_server_name on; proxy_ssl_verify off; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; # Content transformation proxy_set_header Accept-Encoding ""; sub_filter_types text/html text/css text/javascript application/javascript application/json; sub_filter 'href="_assets/' 'href="/assets/'; sub_filter 'src="_assets/' 'src="/assets/'; sub_filter 'url("_assets/' 'url("/assets/'; sub_filter 'content="https://username.my.canva.site/your-site/_assets/' 'content="https://yourdomain.com/assets/'; sub_filter '</head>' '<style>.footer-overflow-container { display: none !important; }</style></head>'; sub_filter_once off; proxy_buffering off; proxy_http_version 1.1; gzip on; gzip_types text/html text/css text/javascript application/javascript; gzip_vary on; } # Assets proxy handler location /assets/ { proxy_pass https://username.my.canva.site/your-site/_assets/; proxy_set_header Host username.my.canva.site; 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; proxy_redirect off; proxy_ssl_server_name on; proxy_ssl_verify off; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; # CORS configuration proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept"; proxy_hide_header Vary; if ($request_method = 'OPTIONS') { return 204; } expires 30d; access_log off; } # DNS resolvers resolver 1.1.1.1 8.8.8.8 valid=300s ipv6=off; # Logs access_log /var/log/nginx/yourdomain.com.access.log; error_log /var/log/nginx/yourdomain.com.error.log; } |
Advanced Customizations
Removing Canva Branding Elements
You can add more CSS to the sub_filter
that adds styles to remove Canva branding elements:
1 2 3 4 5 6 7 8 |
sub_filter '</head>' '<style> .footer-overflow-container, .canva-branding, [data-testid="canva-logo"] { display: none !important; } </style></head>'; |
Handling Forms and Dynamic Content
If your Canva site has forms or dynamic content, you may need additional configuration:
1 2 3 4 5 6 7 8 9 10 11 |
# For handling form submissions location /submit-form/ { proxy_pass https://username.my.canva.site/your-site/submit-form/; proxy_set_header Host username.my.canva.site; # ... other proxy settings # Additional headers for form handling proxy_set_header Content-Type $content_type; proxy_set_header Content-Length $content_length; } |
URL Rewriting and Clean URLs
You might want to implement URL rewriting for cleaner URLs:
1 2 3 |
# Rewrite rule example rewrite ^/page/([a-zA-Z0-9_-]+)$ /your-site/page/$1 break; |
Troubleshooting Common Issues
Assets Not Loading
If assets aren’t loading properly, check:
- CORS headers in the
/assets/
location block - Verify
sub_filter
replacements are correct - Look for hardcoded URLs in Canva’s HTML/CSS/JS
1 2 3 4 5 6 7 |
# Add debug logging for asset requests location /assets/ { # Existing configuration error_log /var/log/nginx/assets-debug.log debug; } |
Content Replacement Issues
If content replacement isn’t working:
- Verify
proxy_buffering off
is set - Check that
sub_filter_once off
is set - Ensure you’re preventing Canva from sending compressed responses
Mixed Content Warnings
If you see mixed content warnings:
1 2 3 4 |
# Add more substitutions to fix mixed content sub_filter 'http://' 'https://'; sub_filter 'ws://' 'wss://'; |
Performance Considerations
Reverse proxying adds some overhead. To optimize performance:
- Enable caching: For static assets and potentially for pages
- Use HTTP/2: Enable HTTP/2 for better performance
- Optimize SSL: Use OCSP stapling and optimize SSL settings
- Consider a CDN: Use Cloudflare or another CDN in front of your NGINX server
1 2 3 4 5 |
# HTTP/2 and performance optimizations listen 443 ssl http2; ssl_stapling on; ssl_stapling_verify on; |
Security Considerations
When reverse proxying, consider these security measures:
- Limit request size: Prevent large requests
- Rate limiting: Protect against abuse
- Security headers: Add headers like Content-Security-Policy
1 2 3 4 5 6 7 8 9 |
# Security enhancements client_max_body_size 10M; limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; limit_req zone=one burst=10 nodelay; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN; add_header X-XSS-Protection "1; mode=block"; |
Conclusion
Reverse engineering a Canva site with NGINX allows you to serve Canva-designed content from your own domain while maintaining full control over your website’s appearance and behavior. By carefully configuring the proxy settings, content transformations, and asset handling, you can create a seamless experience for your users while leveraging Canva’s powerful design platform.
Remember that this approach means you’re responsible for maintaining the NGINX configuration and ensuring it stays compatible with Canva’s site structure. You’ll need to monitor for changes in Canva’s HTML structure or asset paths and adjust your configuration accordingly.
With this setup, you get the best of both worlds: Canva’s easy-to-use design tools and the professional appearance of your own branded domain.