Paystack Mixed Content and HTTPS Errors
Mixed content errors happen when your page loads over HTTP (http://yoursite.com) but tries to load the Paystack script over HTTPS (https://js.paystack.co/v2/inline.js). Browsers block this because an insecure page could tamper with a secure script. The fix is to serve your site over HTTPS. Use Let's Encrypt for free SSL certificates, or enable HTTPS on your hosting platform. You cannot fix this by changing the Paystack script URL to HTTP because Paystack only serves its scripts over HTTPS.
What the Error Looks Like
When your page is served over HTTP and tries to load the Paystack script, browsers show one of these errors:
Mixed Content: The page at 'http://yoursite.com/checkout' was loaded over
an insecure connection. This request has been blocked; the content must be
served over HTTPS.
// Or in Firefox:
Blocked loading mixed active content "https://js.paystack.co/v2/inline.js"
The Paystack script does not load. PaystackPop is undefined. Your checkout button does nothing, or throws a TypeError.
Some developers also see this variation when the page loads over HTTPS but makes API calls or loads images over HTTP:
Mixed Content: The page at 'https://yoursite.com/checkout' was loaded over
HTTPS, but requested an insecure resource 'http://yoursite.com/api/pay'.
This request has been blocked.
Both errors have the same root cause: mixing HTTP and HTTPS on the same page.
Why Browsers Block Mixed Content
When a page loads over HTTP, anyone on the same network (coffee shop WiFi, office network, ISP) can see and modify the page content in transit. If that HTTP page loads a script over HTTPS, the HTTPS protects the script, but the HTTP page could be modified to point to a different, malicious script instead.
Browsers divide mixed content into two categories:
- Active mixed content (scripts, iframes, stylesheets, fetch/XHR calls): blocked completely. This is what happens with Paystack. The browser refuses to load the script.
- Passive mixed content (images, audio, video): shown with a warning but not blocked in most browsers.
Paystack's checkout script is active content. It will always be blocked on an HTTP page. There is no browser setting or workaround that makes this work reliably for your users.
The logic is simple: if you are handling payments, your page must be secure. Every payment provider requires HTTPS. This is not a Paystack-specific requirement.
Fix: Set Up HTTPS with Let's Encrypt
If you manage your own server (VPS on DigitalOcean, Linode, AWS EC2, etc.), the fastest way to get HTTPS is with Let's Encrypt and Certbot. It is free and takes about five minutes.
For Nginx:
# Install Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx
# Get the certificate and auto-configure Nginx
sudo certbot --nginx -d yoursite.com -d www.yoursite.com
# Certbot will:
# 1. Verify you own the domain
# 2. Generate the SSL certificate
# 3. Update your Nginx config to use HTTPS
# 4. Set up auto-redirect from HTTP to HTTPS
# 5. Add a cron job for automatic renewal
For Apache:
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d yoursite.com -d www.yoursite.com
After running Certbot, verify it worked:
# Test HTTPS
curl -I https://yoursite.com
# Should return HTTP/2 200
# Test HTTP redirect
curl -I http://yoursite.com
# Should return HTTP/1.1 301 Moved Permanently
# Location: https://yoursite.com/
Let's Encrypt certificates expire every 90 days. Certbot sets up automatic renewal by default. Test the renewal with sudo certbot renew --dry-run.
Fix: Platform-Specific HTTPS Setup
Most modern hosting platforms provide HTTPS automatically. If you are on one of these platforms and still see mixed content errors, the problem is usually a configuration issue, not a missing certificate.
Vercel: HTTPS is automatic on all deployments. If you see mixed content, your code is generating HTTP URLs. Check for hardcoded http:// links in your HTML or JavaScript. Vercel does not redirect HTTP to HTTPS by default for custom domains, so check your domain settings.
Netlify: HTTPS is automatic. Enable "Force HTTPS" in your domain settings under Domain management > HTTPS.
Railway: HTTPS is automatic on .railway.app domains. For custom domains, Railway provisions certificates automatically after you point your DNS.
Render: HTTPS is automatic. Custom domains get certificates within minutes of DNS configuration.
Heroku: HTTPS is automatic on .herokuapp.com domains. For custom domains, enable Automated Certificate Management (ACM) in your app's Settings tab.
cPanel / shared hosting: Many shared hosts offer free SSL through Let's Encrypt or AutoSSL. Look in your cPanel dashboard under Security > SSL/TLS Status. Click "Run AutoSSL" to provision a certificate. If your host does not offer free SSL, consider moving to a platform that does.
Node.js running directly (no reverse proxy):
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
// Your routes here
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/yoursite.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/yoursite.com/fullchain.pem'),
};
https.createServer(options, app).listen(443, () => {
console.log('HTTPS server running on port 443');
});
// Optional: redirect HTTP to HTTPS
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
In production, it is better to put Nginx or Caddy in front of your Node.js app and let the reverse proxy handle SSL termination.
Force HTTP to HTTPS Redirect
Having HTTPS available is not enough. If users can still access your site over HTTP, they will see mixed content errors on the HTTP version. You need to redirect all HTTP traffic to HTTPS.
Nginx redirect:
server {
listen 80;
server_name yoursite.com www.yoursite.com;
return 301 https://$host$request_uri;
}
Express middleware:
// Force HTTPS in production
app.use((req, res, next) => {
if (
process.env.NODE_ENV === 'production' &&
req.headers['x-forwarded-proto'] !== 'https'
) {
return res.redirect(301, `https://${req.headers.host}${req.url}`);
}
next();
});
Note the x-forwarded-proto check. If your app runs behind a load balancer or reverse proxy (which is common on Railway, Render, Heroku, and AWS), the connection between the proxy and your app might be HTTP even though the user connected over HTTPS. The proxy sets the x-forwarded-proto header to tell your app the original protocol.
Apache .htaccess:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
After setting up the redirect, clear your browser cache and test both http://yoursite.com and https://yoursite.com to confirm the redirect works.
Fix Hardcoded HTTP URLs in Your Code
After enabling HTTPS, check your code for hardcoded http:// URLs. A single HTTP resource on an HTTPS page triggers a mixed content warning.
Common places where hardcoded HTTP URLs hide:
- Image src attributes:
<img src="http://yoursite.com/logo.png"> - API endpoint URLs:
fetch('http://yoursite.com/api/data') - CSS url() references:
background: url('http://yoursite.com/bg.jpg') - Font imports from HTTP CDNs
- Environment variables with HTTP URLs:
API_URL=http://yoursite.com/api - Database records containing HTTP URLs (user avatars, product images)
# Find hardcoded HTTP URLs in your codebase
grep -rn "http://" src/ --include="*.ts" --include="*.tsx" --include="*.js" --include="*.html" --include="*.css"
# Fix by replacing with HTTPS or protocol-relative URLs
# Before: http://yoursite.com/api
# After: https://yoursite.com/api
# Or: /api (relative URL, works on any protocol)
For URLs to external services, always use https://. For URLs to your own site, use relative paths (/api/data instead of https://yoursite.com/api/data). Relative paths automatically use whatever protocol the page was loaded with.
HTTPS in Local Development
During local development, your site usually runs on http://localhost:3000. This is an HTTP URL, so technically it is mixed content. However, browsers treat localhost as a secure context, meaning mixed content restrictions are relaxed for localhost.
Paystack's Inline.js script will load on http://localhost without mixed content errors. You do not need to set up HTTPS for local development.
If you do want HTTPS locally (for testing SSL-specific features), use mkcert:
# Install mkcert
brew install mkcert # macOS
# or: sudo apt install mkcert # Ubuntu
# Create a local certificate authority
mkcert -install
# Generate certificates for localhost
mkcert localhost 127.0.0.1
# Use the certificates in your dev server
# mkcert creates localhost+1.pem and localhost+1-key.pem
Most frameworks support HTTPS in development mode. Next.js has next dev --experimental-https. Vite has the @vitejs/plugin-basic-ssl plugin. But again, for Paystack testing, plain HTTP on localhost works fine.
Verification: Confirm Mixed Content Is Resolved
After enabling HTTPS and fixing hardcoded URLs, verify everything works:
Step 1: Check your site loads over HTTPS.
curl -I https://yoursite.com
# Should return HTTP/2 200 (or HTTP/1.1 200)
# Should NOT return a certificate error
Step 2: Check HTTP redirects to HTTPS.
curl -I http://yoursite.com
# Should return 301 with Location: https://yoursite.com/
Step 3: Check for mixed content in the browser.
Open your checkout page in Chrome. Open DevTools (F12), go to the Console tab. Look for any yellow "Mixed Content" warnings. If you see none, your page is clean.
Step 4: Check the Security tab in DevTools.
Click the "Security" tab in Chrome DevTools. It should show "This page is secure" with a green checkmark. If it shows "This page is not fully secure", click the warning to see which resources are loading over HTTP.
Step 5: Test the Paystack checkout.
// If this works without errors, you are done
const handler = PaystackPop.setup({
key: 'pk_test_your_public_key',
email: 'test@example.com',
amount: 10000,
callback: (response) => console.log('Success:', response.reference),
});
handler.openIframe();
// The popup should open. No console errors.
Step 6: Run an SSL test.
Use SSL Labs Server Test to check your certificate configuration. Aim for an A or A+ rating. This tool will also flag mixed content issues it detects on your page.
Key Takeaways
- ✓Mixed content errors occur when an HTTP page tries to load HTTPS resources like the Paystack script. Browsers block this to prevent insecure pages from tampering with secure content.
- ✓You cannot fix this by changing the Paystack script URL to HTTP. Paystack only serves scripts over HTTPS, and loading payment scripts over HTTP would be a security risk.
- ✓The only real fix is to serve your site over HTTPS. Let's Encrypt provides free SSL certificates that auto-renew.
- ✓Most hosting platforms (Vercel, Netlify, Railway, Render, Heroku) provide HTTPS automatically. If you are on one of these platforms and still see the error, check that you are not forcing HTTP in your code.
- ✓After enabling HTTPS, update all hardcoded http:// URLs in your code to https:// or use protocol-relative URLs (//). A single HTTP resource on an HTTPS page will trigger a mixed content warning.
- ✓Paystack requires HTTPS for live mode. Even if you get the checkout working on HTTP in test mode, live payments will not work without HTTPS.
Frequently Asked Questions
- Can I load the Paystack script over HTTP instead of HTTPS?
- No. Paystack only serves its scripts over HTTPS. Even if you write the script tag as http://js.paystack.co/v2/inline.js, Paystack redirects to HTTPS. Loading a payment script over HTTP would be a security risk because the script could be modified in transit by anyone on the network.
- Does Paystack work on localhost without HTTPS?
- Yes. Browsers treat localhost as a secure context, so mixed content restrictions are relaxed. The Paystack checkout popup will work on http://localhost:3000 during development. You only need HTTPS on your production domain.
- My hosting provider charges for SSL certificates. Is there a free option?
- Yes. Let's Encrypt provides free SSL certificates. Most modern hosting platforms (Vercel, Netlify, Railway, Render) include free SSL automatically. If your current host charges for SSL and does not support Let's Encrypt, consider migrating to a platform that provides HTTPS at no extra cost.
- I enabled HTTPS but my site still loads over HTTP. What is wrong?
- You need to set up an HTTP to HTTPS redirect. Having a certificate installed does not automatically redirect HTTP traffic. Configure your web server (Nginx, Apache) or hosting platform to redirect all HTTP requests to HTTPS with a 301 redirect. Also check for bookmarks, links, or hardcoded URLs that point to the HTTP version.
- Will HTTPS slow down my site?
- No. Modern TLS adds negligible latency (usually under 5ms for the handshake). HTTPS actually enables HTTP/2, which is faster than HTTP/1.1 for loading multiple resources. Any modern site should use HTTPS regardless of whether it handles payments.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon