- Overview
- Features
- Prerequisites
- Google Cloud Platform (GCP) Setup
- Cloudflare Setup
- HTML Files for Wix website encapsulation
- Bonus - Weather APIs
This guide demonstrates how to:
- Set up a custom domain and configure DNS.
- Set up a static website with Google Cloud Storage that will be reached via a custom domain.
- Host your site using Google Cloud Storage.
- Configure SSL, Namespace and Firewall through Cloudflare.
- iframe manipulation
- Implement APIs in Wix Velo
The guide is intended for techincal and non-technical audience. Steps requiring more in-depth knoledge are marked with a ✬ symbol.
-
Custom Domain Integration
Connect your domain to your Wix site by hosting a static website on Google Cloud Storage Bucket. -
Static Hosting
Use Google Cloud Storage as a static website host. -
Secure Website
Implement HTTPS and WAF with Cloudflare.
To use this guide, you'll need:
- A registered domain name.
- Access to Google Cloud Platform (GCP).
- A Cloudflare account.
- Basic understanding of DNS and hosting.
- Purchase and register a domain. Namecheap or GoDaddy are usually suggested.
-
Register domain ownership
- We need to create a Google Cloud Storage instance with the same name as our website. In order to do this, we need to prove Google we own the domain by adding a record to our DNS.
- Visit Google Search Console
- Fill in your domain name under the Domain section.
- Click CONTINUE and then select CNAME as verification method.
- Add a CNAME record to your DNS with the provided data.
-
Set up a GCP project
- GCP Free trial credit can be used for our purposes.
-
Set up a GCP bucket for hosting
-
An official Google documentation page can be accessed at Hosting a static website using HTTP. Our procedure will differ in how public access is granted to each file.
-
Use the same name as the registered domain with the format
www.my-domain.com
. -
For our private-use, research-purpose project, we select a single region, like
us-central1
. -
Deselect Enforce public access prevention on this bucket as we will be granting our file public access.
-
Additional but not required: Create a storage acting as a backup with the
NEARLINE
storage class for reducing costs and set up a replication policy. -
✬ Equivalent gcloud commands:
# Set the project name PROJECT_ID=<YOUR_PROJECT_ID> # Set the main bucket MAIN_BUCKET_NAME="www.my-domain.com" #Change it to your registered domain # Set the bucket zone BUCKET_ZONE="us-central1" # Dynamically create the backup bucket name BACKUP_BUCKET_NAME="backup-${MAIN_BUCKET_NAME}" # Dynamically get the project ID PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)") # Set the active project gcloud config set project "$PROJECT_ID" # Create the main bucket gcloud storage buckets create "gs://${MAIN_BUCKET_NAME}" \ --location=${BUCKET_ZONE} \ --default-storage-class=STANDARD \ --no-public-access-prevention \ --no-uniform-bucket-level-access # Configure access to use index.html as the main page gcloud storage buckets update "gs://${MAIN_BUCKET_NAME}" --web-main-page-suffix=index.html # Create the backup bucket gcloud storage buckets create "gs://${BACKUP_BUCKET_NAME}" \ --location=${BUCKET_ZONE} \ --default-storage-class=NEARLINE \ --public-access-prevention \ --uniform-bucket-level-access # Enable the Storage Transfer Service API gcloud services enable storagetransfer.googleapis.com # Set up IAM roles for data transfer TRANSFER_SERVICE_ACCOUNT="project-${PROJECT_NUMBER}@storage-transfer-service.iam.gserviceaccount.com" STORAGE_SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gs-project-accounts.iam.gserviceaccount.com" # Grant IAM roles for the main bucket gcloud storage buckets add-iam-policy-binding "gs://${MAIN_BUCKET_NAME}" \ --member="serviceAccount:${TRANSFER_SERVICE_ACCOUNT}" \ --role="roles/storage.admin" # Grant IAM roles to the project for data transfer gcloud projects add-iam-policy-binding "$PROJECT_NUMBER" \ --member="serviceAccount:${TRANSFER_SERVICE_ACCOUNT}" \ --role="roles/storage.admin" gcloud projects add-iam-policy-binding "$PROJECT_NUMBER" \ --member="serviceAccount:${TRANSFER_SERVICE_ACCOUNT}" \ --role="roles/pubsub.editor" # Grant IAM roles to the Cloud Storage service account gcloud projects add-iam-policy-binding "$PROJECT_NUMBER" \ --member="serviceAccount:${STORAGE_SERVICE_ACCOUNT}" \ --role="roles/pubsub.publisher" # Create the replication job gcloud alpha transfer jobs create "gs://${MAIN_BUCKET_NAME}" "gs://${BACKUP_BUCKET_NAME}" --replication # List active replication jobs gcloud alpha transfer jobs list --job-type=replication
-
✬ A Terraform equivalent, including the
index.html
andmobile.html
creation, can be found at main.tf.
-
This section covers the configuration of HTTPS, DNS, nameservers, and Web Application Firewall (WAF) rules using Cloudflare. The goal is to secure your static website hosted on Google Cloud Platform (GCP) and ensure proper traffic handling.
- A Cloudflare account.
- The domain name registered and added to your Cloudflare account.
- Access to the DNS management settings in your registrar's dashboard.
-
Add Your Domain to Cloudflare
- Log in to your Cloudflare account.
- Click on Add a Site and enter your domain name (e.g.,
my-domain.com
). - Cloudflare will scan your existing DNS records. Verify them and make necessary adjustments.
-
Set Up DNS Records
-
Add a CNAME record pointing from
www.my-domain.com
to the GCP bucket hosting your static website:Name Type Content TTL www CNAME c.storage.googleapis.com Auto - Import file alternative (change content with your domain name)
- ✬ CLI alternative (using Cloudflare API):
curl -X POST "https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/dns_records" \ -H "Authorization: Bearer <API_TOKEN>" \ -H "Content-Type: application/json" \ --data '{ "type":"CNAME", "name":"www.my-domain.com", "content":"c.storage.googleapis.com", "ttl":1, "proxied":true }'
-
-
Change Nameservers
- Cloudflare will provide two nameservers (e.g.,
ns1.cloudflare.com
andns2.cloudflare.com
) under the page DNS/Records. - Update your registrar's DNS settings to point to these nameservers. This change may take up to 48 hours to propagate.
- Cloudflare will provide two nameservers (e.g.,
-
Enable HTTPS
- In Cloudflare's SSL/TLS section, set the mode to Full or Full (strict) for secure communication.
- Toggle Always Use HTTPS to automatically redirect HTTP traffic to HTTPS.
-
Add Redirect Rules
- Go to the Rules section and create the following redirects:
-
Redirect HTTP to HTTPS
Match:http://*/*
Redirect to:https://$1/$2
(Enable "Forwarding URL") -
Redirect Root HTTPS to WWW
Match:https://my-domain.com/*
Redirect to:https://www.my-domain.com/$1
(Enable "Forwarding URL")
-
- Go to the Rules section and create the following redirects:
-
Restrict Traffic to Specific Countries
- Rule: Allow traffic only from specified country.
- Expression example:
not ip.geoip.country in {"GB" "US" "DE"}
-
Block Access to Unwanted Pages
- Rule: Block all pages except the allowed ones.
- Expression:
not ends_with(http.request.full_uri, "/") and not ends_with(http.request.full_uri, "/favicon.ico") and not ends_with(http.request.full_uri, "/mobile.html")
-
Block Suspicious User Agents
- Rule: Block bots, crawlers, and headless browsers.
- Expression:
(http.user_agent contains "bot") or (http.user_agent contains "crawl") or (http.user_agent contains "spider") or (http.user_agent contains "Headless") or (not ssl)
-
Block Based on Threat Score
- Rule: Block if threat_score >= 1
- Expression:
cf.threat_score ge 1
-
Block Known Bots
- Rule: Block all known bots.
- Enable the "Known Bots" option in Cloudflare WAF.
- Verify that
www.my-domain.com
resolves correctly and loads your static website. - Ensure HTTPS is enforced and all redirects work as expected.
- Test the WAF rules using different countries, user agents, and URLs.
This setup ensures secure and optimized traffic handling for your custom domain, leveraging Cloudflare's powerful tools to enhance the functionality and security of your site.
This section presents the two index.html
and mobile.html
files. The index.html
is pretty straightforward, it contains a shifted iframe pointing at the wix webiste and a redirect to mobile.html when the screen with is less then 900px.
The mobile.html
is a bit more complex as Wix forces a 320px width that results in a non-responsive resizing. This is addressed through zooming in/out the iframe as screen sizes changes.
Note: I implemented the zooming feature using some help from AI, it may be enhanced more
-
index.html
- switch Wix url with your actual Wix website url
- select a custom title
<!DOCTYPE html> <html lang="it"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My title</title> <link rel="icon" href="https://storage.googleapis.com/www.my-website.com/favicon.ico" type="image/x-icon"> </head> <body> <iframe src="https://my.wixsite.com/" style="position:fixed; top:-50px; left:0px; bottom:0px; right:0px; width:100%; height:105%; "> </iframe> <script type="text/javascript"> if (screen.width <= 900) { document.location = "/mobile.html"; } </script> </body> </html>
-
mobile.html
- switch Wix url with your actual Wix website url
- select a custom title
<!DOCTYPE html> <html lang="it"> <head> <meta charset="UTF-8"> <title>My title</title> <link rel="icon" href="https://storage.googleapis.com/www.my-website.com/favicon.ico" type="image/x-icon"> <meta name="theme-color" content="#6E1633"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> /* Hide only the Wix ad banner */ iframe { position: fixed; left: 0; top: -39px; /* Adjust as needed to hide the banner */ width: 100%; height: calc(100% + 39px); /* Adjusted to cover the area hidden by the banner */ border: none; } /* Remove any padding and margin */ body { margin: 0; padding: 0; background-color:#E9CAB1; } </style> </head> <body> <iframe src="https://my.wixsite.com/" style="position: fixed; left: 0px; top: -39px; width: 100%; height: 108%;"> </iframe> <script> function resizeIframe() { const iframe = document.querySelector('iframe'); const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; // Define the base width and top offset of the Wix content const baseWidth = 320; // Original width of the Wix content const baseTop = -39; // Original `top` offset const scaleFactor = screenWidth / baseWidth; // Apply scaling to the iframe iframe.style.transform = `scale(${scaleFactor})`; iframe.style.transformOrigin = "top left"; // Adjust iframe's dimensions and position iframe.style.width = `${baseWidth}px`; // Original content width const newHeight = (screenHeight / scaleFactor) - (baseTop * scaleFactor); // Compensate for banner removal iframe.style.height = `${newHeight}px`; // Scaled height to fit the viewport iframe.style.left = `calc((100vw - ${baseWidth}px * ${scaleFactor}) / 2)`; // Center horizontally iframe.style.top = `${baseTop * scaleFactor}px`; // Scale the top offset } // Run resize function immediately after DOM is loaded document.addEventListener('DOMContentLoaded', resizeIframe); // Resize on window resize events (debounced for better performance) let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(resizeIframe, 100); }); </script> </body> </html>
-
favicon.ico
- you can upload a custom favicon.ico to the bucket and reference it into index.html and mobile.html
An implementation of The Norwegian Meteorological Institute Weather API can be found in /velo/weather.js. It is leveraged to present a weather widget.