• Halloween is coming! To celebrate the occasion, we dusted off a couple of old skins, and even created a brand new one (Pixel).
    To get into the festive spirit, install the Skins extension.
  • The new Python extension is now available. It allows customers to deploy and manage WSGI-based Python applications on their websites directly from Plesk.
  • The Plesk Mobile apps for iOS and Android and the Plesk Mobile Center extension have been deprecated, and are no longer available for new installations. In Plesk Obsidian 18.0.84, the Plesk Mobile Center extension will be removed from servers during the upgrade, and the Plesk Mobile apps will lose their server management features.
  • The Social Login extension has been deprecated and will be removed in Plesk Obsidian 18.0.84. After that, it will no longer be possible to log in to Plesk with a Google, Facebook, GitHub account, or with a passkey. Plesk usernames and passwords, as well as logging in with a WebPros Account, are not affected.

Issue Subdomains get a 403 error

therider62

New Pleskian
I am running a multisite-wordpress installation with about 30 sites, half a dozen of which are mapped to 2nd-level-domains aliases, the rest are subdomains of the master domain.

I created a wildcard domain as an alias to the master domain, as well as the various 2nd-level-alias-domains.

All of them are secured by one Let's-Encrypt certificate. Everything ran smoothly for the past several months.

One of the 2nd-level-alias-domains was transferred away to another provider. Next time the certificate expired, it could not be renewed, because that particular 2nd-level-alias failed to comply with requirements. I deleted that alias, and this time the certificate renewal worked like a charm. Wonderful!

But now I noticed that the subdomain sites can't be opened anymore. The main site opens, the 2nd-level-domain sites open too, but the subdomain sites receive a 403 error.

1. I checked the .htaccess and could not find a problem there.
2. I checked "Protocol", and am not receiving an error there.
3. I activated DEBUG output in the WP toolkit, but did not get any output from there either.
4. The main domain has the correct certificate selected and activated. The certificate contains the wildcard domain as well, plus all the 2nd-level-domains.
5. I don't think this has anything to do with the WP Toolkit, but one can never be sure, of course.

Does anyone have an idea what could be the problem here and why this is popping up right now? Or where I should go looking for what kind of configuration error?

Of course, I am updating my wordpress installation all the time, as well as all the themes and plugins that go along with it. But I can not imagine that that could lead to a 403 error.
 
@therider62 , could you please monitor the following logs when you reproduce the error and confirm what's being logged:

/var/www/vhosts/maindomain.com/logs/error_log
/var/www/vhosts/subdomain.maindomain.com/logs/error_log
 
@therider62 , could you please monitor the following logs when you reproduce the error and confirm what's being logged:

/var/www/vhosts/maindomain.com/logs/error_log
/var/www/vhosts/subdomain.maindomain.com/logs/error_log
Thank you for your request. This is interesting.

* /var/www/vhosts/maindomain.com/logs/error_log does not produce any output, when I refresh the subdomain, that is producing the 403.
The only error I can see, but I am not 100% sure if it was triggered by my refresh, is this:

[Wed Nov 04 17:22:03.751229 2026] [core:error] [pid 3156918:tid 139805295875776] [client 193.32.127.215:0] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.

* /var/www/vhosts/subdomain.maindomain.com/ does not exist. I am assuming this is because the subdomain is only a site within the multisite wordpress. I never added it explicitly as a subsite to PLESK. I *did* add a general purpose subsite "*", or the wildcard domain, meaning "*.maindomain.com".
 
Hey @therider62,

that "10 internal redirects" line (AH00124) is a loop inside Apache. A loop usually ends in a 500 though, not a 403, so I wouldn't be sure yet that this is your problem. First thing to find out is who actually throws the 403. Try this:
Code:
curl -skI https://sub.maindomain.com/

Look at the "Server" header and the body of the response (nginx, Apache and WordPress all have different-looking 403 pages). To rule out DNS, you can also pin the request to your server IP:
Code:
curl -skI --resolve sub.maindomain.com:443:YOUR_SERVER_IP https://sub.maindomain.com/

Since the subdomains are only a wildcard alias of the main domain, they don't get their own log folder - their requests end up in the main domain's logs. Watch these while you reload the subdomain (the system-level folder also includes the nginx proxy logs):
Code:
tail -f /var/www/vhosts/system/maindomain.com/logs/proxy_access_ssl_log /var/www/vhosts/system/maindomain.com/logs/access_ssl_log /var/www/vhosts/system/maindomain.com/logs/error_log /var/www/vhosts/system/maindomain.com/logs/proxy_error_log

If the request shows up in an access log with a 403, you know which layer answers. If it shows up nowhere, check the server-wide logs too:
Code:
tail -f /var/log/nginx/error.log /var/log/apache2/error.log

Next, since this started right after you deleted the alias and the cert got renewed: Plesk regenerated the vhost configs in that process. Check whether the wildcard alias is still in the generated configs. You should see *.maindomain.com in both:
Code:
grep -i "ServerAlias" /etc/apache2/plesk.conf.d/vhosts/maindomain.com.conf
Code:
grep -i "server_name" /var/www/vhosts/system/maindomain.com/conf/nginx.conf

Then let Plesk rebuild the web config for that domain and test again:
Code:
plesk repair web maindomain.com -y

or, the older way:
Code:
plesk sbin httpdmng --reconfigure-domain maindomain.com

Two more things worth checking in the panel:

- In the settings of the wildcard alias: is an alias redirect option ("Redirect with the 301 code" or similar) enabled? Combined with an HTTP->HTTPS redirect and the WordPress multisite redirects, that could build exactly this kind of loop. That's only a guess on my part, not something I can confirm from here.
- Hosting & DNS > Apache & nginx Settings: any additional directives that block or redirect?

If the 403 turns out to come from a security layer (ModSecurity / WAF, fail2ban, Imunify), the logs will show it. For ModSecurity the audit log is usually here:
Code:
tail -f /var/log/modsec_audit.log

To get more detail on the redirect loop, you can temporarily add this to the additional Apache directives of the domain (remove it afterwards, it's noisy):
Code:
LogLevel debug

Let us know what the curl output and the logs say, then we can narrow it down.

Regards
Tessa
 
OK, @tessa67, thank you for your suggestions. Let me go through them one by one.

`$ curl -skI https://sub.maindomain.com/
HTTP/2 403
server: nginx
date: Wed, 04 Nov 2026 18:28:30 GMT
content-type: text/html
content-length: 795
last-modified: Sun, 22 Jun 2025 06:00:01 GMT
etag: "31b-63822ce494b6e"
accept-ranges: bytes`

$ curl -skI --resolve sub.main.com:443:MY-SERVER-IP https://sub.main.com/
HTTP/2 403
server: nginx
date: Wed, 04 Nov 2026 18:30:36 GMT
content-type: text/html
content-length: 795
last-modified: Sun, 22 Jun 2025 06:00:01 GMT
etag: "31b-63822ce494b6e"
accept-ranges: bytes
$ tail -f /var/www/vhosts/system/blog-net.ch/logs/proxy_access_ssl_log /var/www/vhosts/system/blog-net.ch/logs/access_ssl_log /var/www/vhosts/system/blog-net.ch/logs/error_log /var/www/vhosts/system/blog-net.ch/logs/proxy_error_log
==> no output

$ tail -f /var/log/nginx/error.log /var/log/apache2/error.log
==> no output

$ grep -i "ServerAlias" /etc/apache2/plesk.conf.d/vhosts/blog-net.ch.conf
==> gives a long list of all known domains (main.com, alternate 2nd-level-domains), but NOT the sub.main.com! And NOT the wildcard domain!

$ grep -i "server_name" /var/www/vhosts/system/blog-net.ch/conf/nginx.conf
==> same list, it seems

$ plesk repair web maindomain.com -y
==> runs through nicely.
==> But after refreshing sub.main.com, still get 403 error

$ root@v2202411195655298871:~# tail -f /var/log/modsec_audit.log
Apache-Error: [file "apache2_util.c"] [line 286] [level 3] ModSecurity: Access denied with code 403 (phase 1). Matched phrase "/.git/" at REQUEST_URI. [file "/etc/apache2/modsecurity.d/rules/comodo_free/02_Global_Generic.conf"] [line "117"] [id "210492"] [rev "3"] [severity "CRITICAL"] [tag "CWAF"] [tag "Generic"] [hostname "webmail.rudolflemcke.ch"] [uri "/.git/config"] [unique_id "auuEyv93mrI6f_vdunR-3AAAAMA"]
Action: Intercepted (phase 1)
Stopwatch: 1793819850737521 708 (- - -)
Stopwatch2: 1793819850737521 708; combined=283, p1=221, p2=0, p3=0, p4=0, p5=62, sr=101, sw=0, l=0, gc=0
Producer: ModSecurity for Apache/2.9.15 (http://www.modsecurity.org/); CWAF_Apache.
Server: Apache
Engine-Mode: "ENABLED"

==> This may be the only one that is point somewhere. But I am not sure what it means. Can you help me?
 
Hey @therider62,

thanks for the detailed output, that helps a lot.

About the ModSecurity entry: I'd ignore it for now. It's for a different hostname and the URI is /.git/config, which is just a bot probing for exposed git folders (the Comodo rule blocks that on purpose). Also, ModSecurity belongs to Apache, but your 403 is answered by nginx itself.

The interesting part is this: the 403 page has a Last-Modified date from June 2025 plus an ETag. That looks like a static file served straight from disk by nginx, so WordPress and Apache aren't involved at all. Combined with your grep results (neither sub.main.com nor the wildcard shows up in ServerAlias / server_name), my guess is that the request never reaches your domain's vhost and lands in some other nginx server block, for example the default one for unknown hostnames. That would also explain why none of the domain logs show anything. Just a hypothesis for now, but it fits.

Since "plesk repair web" rebuilds the configs from Plesk's database and the wildcard is still missing afterwards, I'd first check whether the wildcard still exists in Plesk at all, and in what form. In your first post you wrote "alias", later "subsite *", so please check both places in the panel: Domains > maindomain.com > Domain Aliases, and the list of Subdomains. Is *.maindomain.com in one of them?

Quick check from the console, if the wildcard is in any generated config at all:
Code:
grep -rlF '*.maindomain.com' /etc/apache2/plesk.conf.d/ /etc/nginx/plesk.conf.d/ /var/www/vhosts/system/maindomain.com/conf/

What nginx actually has loaded right now:
Code:
nginx -T 2>/dev/null | grep -n -F '*.maindomain.com'

If a wildcard subdomain exists, it may have its own folder with a literal asterisk in the name:
Code:
ls /var/www/vhosts/system/ | grep -F '*'

And to see what the 403 page says (it's only about 800 bytes, so it should tell us which page that is):
Code:
curl -sk https://sub.maindomain.com/

If the nginx log file exists on your box, watch it while you reload the subdomain, since a catch-all block would log there and not in the domain's logs:
Code:
tail -f /var/log/nginx/access.log

If the wildcard is gone in the panel: add it again the same way you had it before, then rebuild the config:
Code:
plesk repair web maindomain.com -y

If it's still there in the panel but never ends up in the generated nginx/Apache configs, that would point to a problem on Plesk's side, and I'd open a support ticket with the outputs above.

Let us know what you find.

Regards
Tessa
 
@tessa67 ist it okay to post my real domain names here, so we can easier analyse the actual situation? Or is that a definite no-no? I believe it would make our analysis part a lot easier. For example, I think you should have a quick look at the actual Let's-Encrypt-certificate, and then I can show you screenshots of what my PLESK screens actually look like.
 
@tessa67 ist it okay to post my real domain names here, so we can easier analyse the actual situation? Or is that a definite no-no? I believe it would make our analysis part a lot easier. For example, I think you should have a quick look at the actual Let's-Encrypt-certificate, and then I can show you screenshots of what my PLESK screens actually look like.
Hi, @therider62

while it isn't strictly forbidden to post your real domains, it is highly discouraged for security and privacy reasons, as publicizing server issues can attract attackers and lead to spam or unwanted Google indexing. Instead, it is much safer to use placeholders like example.com and blur out sensitive data in your screenshots while still sharing the relevant Plesk configuration details.

Regards
Tessa
 
@tessa67 okay, I will try to remember to keep anonymizing my domain names. :)

1. The wildcard domain name is featured in the PLESK "Domains" screen, as well as in the main.com "Subscription" screen, where the wildcard domain as well as all the associated 2nd-level-domains are listed below the main domain. This clean structure explains, I believe, why the Let's-Encrypt-SSL-certificate is created very clean, stating it is for the main domain, but also valid for the wildcard and all the 2nd-level-domains.

2. I do not know or understand, how the wildcard then is introduced (or missed) into the Nginx and/or Apache configuration.

Code:
grep -rlF '*.blog-net.ch' /etc/apache2/plesk.conf.d/ /etc/nginx/plesk.conf.d/ /var/www/vhosts/system/maindomain.com/conf/
==> empty output

Code:
nginx -T 2>/dev/null | grep -n -F '*.maindomain.com'
==> empty output

Code:
nginx -T 2>/dev/null | grep -n -F 'maindomain.com'
==> without the asterisk, produces lots of output, quite a number of which mention "_.maindomain.com"

Code:
ls /var/www/vhosts/system/ | grep -F '*'
==> empty output

Code:
curl -sk https://steuern.maindomain.com/
==> produces this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>403 Forbidden</title>
<link rel="stylesheet" href="/error_docs/styles.css">
</head>
<body>
<div class="page">
<div class="main">
<h1>Server Error</h1>
<div class="error-code">403</div>
<h2>Forbidden</h2>
<p class="lead">You do not have permission to access this document.</p>
<hr/>
<p>That's what you can do</p>
<div class="help-actions">
<a href="javascript:location.reload();">Reload Page</a>
<a href="javascript:history.back();">Back to Previous Page</a>
<a href="/">Home Page</a>
</div>
</div>
</div>
</body>
</html>

Code:
tail -f /var/log/nginx/access.log
==> produces no output upon refreshing the 403 URL

Code:
plesk repair web maindomain.com -y
==> produces this:
Checking web server configuration. Please wait
Repairing web server configuration for the domain maindomain.com ... [OK]

Checking php-fpm configuration
Repairing php-fpm configuration for the domain maindomain.com ...... [OK]

Checking for missing log rotation configuration files ............... [OK]

Error messages: 0; Warnings: 0; Errors resolved: 0
==> But refreshing the 403 URL still produces the 403 page.
 
All these domains point at the same servder directory /httpdocs, because the multisite wordpress installation takes care of rendering all of them individually, of course.
 
Back
Top