405 Method not allowed when using php email form

1. The problem I’m having:

I have a landing page running via file-server. This landing page has an email form included that should allow users to send me messages. However, when I try to use this, I get the error “405 Method not allowed”. The page in question can be found under build.{$DOMAIN} in the caddyfile


error message
The web developer tools are saying, that get and head are allowed. How do I allow POST with a file_server in caddy so that I can send mails from my website? I already tried to add
@post method POST but that didn’t change anything

2. Error messages and/or full log output:

caddys docker container log:

DBG undefined | ts=1678698513.5970645 logger=http.handlers.file_server msg=opening file filename=/usr/share/caddy/index.html 
DBG undefined | ts=1678698539.7682939 logger=http.handlers.reverse_proxy msg=selected upstream dial=192.168.0.28:909 total_upstreams=1 
DBG undefined | ts=1678698539.7818184 logger=http.handlers.reverse_proxy msg=upstream roundtrip upstream=192.168.0.28:909 duration=0.013351433 request=[object Object] headers=[object Object] status=200 
DBG undefined | ts=1678698540.6130097 logger=http.handlers.file_server msg=sanitized path join site_root=/usr/share/caddy/ request_path=/inc/sendEmail.php result=/usr/share/caddy/inc/sendEmail.php 
DBG undefined | ts=1678698540.6131046 logger=http.handlers.file_server msg=opening file filename=/usr/share/caddy/inc/sendEmail.php 
DBG undefined | ts=1678698540.6133387 logger=http.log.error msg={id=z1580cbra} fileserver.(*FileServer).ServeHTTP (staticfiles.go:418): HTTP 405 request=[object Object] duration=0.000410947 status=405 err_id=z1580cbra err_trace=fileserver.(*FileServer).ServeHTTP (staticfiles.go:418) 
DBG undefined | ts=1678698541.94272 logger=http.handlers.reverse_proxy msg=selected upstream dial=192.168.0.28:909 total_upstreams=1 

3. Caddy version:

  • v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

4. How I installed and ran Caddy:

docker run -d -p 80:80 -p 443:443 -p 443:443/udp -v ./data:/data -v ./config:/config -v ./Caddyfile:/etc/caddy/Caddyfile -v ./logs:/var/log/caddy --restart=unless-stopped --name=caddy caddy

a. System environment:

Docker version: 23.0.0
Fedora release 37 (Thirty Seven)
on a Asus X555LB 1.0

d. My complete Caddy config:

# global options   
{
	email admin@example.de
	debug
}

# internal
*.{$LCLDMN} {
	tls internal
}

admin.{$LCLDMN} {
	reverse_proxy 192.168.0.28:8020
}

wol.{$LCLDMN} {
	reverse_proxy 192.168.0.28:7777
}

speed.{$LCLDMN} {
	reverse_proxy 192.168.10.35
}

disk.{$LCLDMN} {
	reverse_proxy 192.168.0.28:9095
}

snips.{$LCLDMN} {
	reverse_proxy 192.168.10.11:5000
}

backup.{$LCLDMN} {
	reverse_proxy 192.168.0.28:8200
}

port.{$LCLDMN} {
	reverse_proxy 192.168.10.5
}

dashboard.{$LCLDMN} {
	reverse_proxy 192.168.10.21
}

dia.{$LCLDMN} {
	reverse_proxy 192.168.0.28:8888
}

prometheus.{$LCLDMN} {
	reverse_proxy 192.168.0.28:9050
}

remote.{$LCLDMN} {
	reverse_proxy 192.168.10.30:8080
}

calibre.{$LCLDMN} {
	reverse_proxy 192.168.10.14
}

fritz.box {
	tls internal
}

#external
www.{$DOMAIN} {
	redir https://{$DOMAIN}{uri}
}

build.{$DOMAIN} {
	root * /usr/share/caddy/
	file_server
	@post method POST
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

pass.{$DOMAIN} {
	reverse_proxy 192.168.0.28:8010
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
	reverse_proxy https://home.lab {
		header_up Host {upstream_hostport}
	}
}

code.{$DOMAIN} {
	reverse_proxy 192.168.0.28:8443
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

msg.{$DOMAIN} {
	reverse_proxy 192.168.0.28:8090
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

health.{$DOMAIN} {
	reverse_proxy 192.168.0.28:3000
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

active.{$DOMAIN} {
	reverse_proxy 192.168.0.28:3001
	encode zstd gzip
	header {
		Access-Control-Allow-Origin: *
		Access-Control-Allow-Methods "OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE"
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

nc.{$DOMAIN} {
	rewrite /.well-known/carddav /remote.php/dav
	rewrite /.well-known/caldav /remote.php/dav

	reverse_proxy 192.168.0.28:8070

	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
	}
}

chat.{$DOMAIN} {
	reverse_proxy 192.168.0.28:3030
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
	}
}

foto.{$DOMAIN} {
	reverse_proxy 192.168.0.28:2283
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
	}
}

bookmark.{$DOMAIN} {
	reverse_proxy 192.168.0.28:909
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
	}
}

cyber.{$DOMAIN} {
	reverse_proxy 192.168.10.33:8000
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
	}
}

read.{$DOMAIN} {
	reverse_proxy 192.168.0.28:5000
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		Referrer-Policy no-referrer-when-downgrade
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
	}
}

You need to send the request to PHP:

Yes thanks. I installed php_fpm as a container last night. My website has only 1 php file and it is in another folder than the index.
The docker-compose file of php_fpm is

services:
  php:
    image: docker.io/bitnami/php-fpm:8.2
    ports:
      - 9023:9000
    volumes:
      - /home/huddeij/homepage:/app

(the sendEmail.php is in the subfolder homepage/inc)
I made sure caddy and php_fpm are in the same network

and the relevant part of my caddyfile is:

build.{$DOMAIN} {
	root * /usr/share/caddy/
	file_server
	php_fastcgi 192.168.0.28:9023
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

the error message is now
image

the log file output:

DBG undefined | ts=1678784990.1554873 logger=http.handlers.reverse_proxy msg=selected upstream dial=192.168.0.28:9023 total_upstreams=1 
DBG undefined | ts=1678784990.1560087 logger=http.reverse_proxy.transport.fastcgi msg=roundtrip request=[object Object] env=[object Object] dial=192.168.0.28:9023 
DBG undefined | ts=1678784990.1583502 logger=http.handlers.reverse_proxy msg=upstream roundtrip upstream=192.168.0.28:9023 duration=0.00271652 request=[object Object] headers=[object Object] status=404 

I already tried:

  • to change the caddyfile to php_fastcgi / 192.168.0.28:9023 which brought back the old error message (504)
  • commented out root * /usr/share/caddy/ to only use php and renamed the index.html to index.php, but this blanked my page
  • and changed the php_fpm docker compose file to - /home/huddeij/homepage/inc:/app. this brought me back to the original error message (405)

You need to mount your app’s files to Caddy’s container so it can access them. I don’t see that in your original post.

I recommend your mount your files to /srv and use root * /srv to tell Caddy to serve those.

Make sure the files are mounted to the same place in both the PHP container and the Caddy container. The file paths need to match since Caddy will ask the PHP container to run the file at a particular path.

The /usr/share/caddy is a folder from inside caddy’s container via volume.


php-fpm’s app volume is linked to the same folder.

THe file paths outside of the container are the same. Should I match them inside the container aswell? That doesn’t make a lot of sense to me and I don’t know how

Yes, like I said, they must be the same paths in both containers, since Caddy takes the path to the files it sees and sends that to PHP to run.

I changed the inside folder of caddy’s access to the website to match the structure of php-fpm’s folder structure
php-fpm:

caddy:

caddyfile:

build.{$DOMAIN} {
	root * /app
	php_fastcgi 127.0.0.1:9000
	file_server
	encode zstd gzip
	header {
		Strict-Transport-Security max-age=15552000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy no-referrer-when-downgrade
	}
}

After a graceful reload the page works, but when I use the php email form I get 502:
error message#

I already tried to change the caddyfile entry, with the same result:

  • php_fastcgi /inc/*.php 127.0.0.1:9000
  • php_fastcgi *.php 127.0.0.1:9000
  • php_fastcgi /*.php 127.0.0.1:9000
  • php_fastcgi sendEmail.php 127.0.0.1:9000
  • php_fastcgi inc/sendEmail.php 127.0.0.1:9000
  • php_fastcgi /inc/sendEmail.php 127.0.0.1:9000

I also renamed the index.html to index.php and tried php_fastcgi 127.0.0.1:9000 but this delivered a blank page with 502 error

In Docker, 127.0.0.1 means “this container”. PHP is running in a different container. You need to connect to the other container using its service name. Use php:9000 instead.

Yes thanks, that helped. Thank you!
But, now I have an error I cannot follow nor describe. The email form just spits “Something went wrong.” out. I think, that doesn’t have anything to do with php-fpm, so I call that solved. It’s even answering with code 200! Awesome, thank you for your persistant patience!
image

I just setup a xampp server on my local pc and tried it there and tadaa It worked fine.
image
What could block the message sending in this particular situation? Port not opened? Security standards in caddy that prevents the sending? My php code is utterly outdated?
Anything what comes to mind helps I guess

<?php

// Replace this with your own email address
$siteOwnersEmail = 'application@huddeij.de';


if($_POST) {

   $name = trim(stripslashes($_POST['contactName']));
   $email = trim(stripslashes($_POST['contactEmail']));
   $subject = trim(stripslashes($_POST['contactSubject']));
   $contact_message = trim(stripslashes($_POST['contactMessage']));
   $error = "";

   // Check Name
	if (strlen($name) < 2) {
		$error['name'] = "Please enter your name.";
	}
	// Check Email
	if (!preg_match('/^[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*+[a-z]{2}/is', $email)) {
		$error['email'] = "Please enter a valid email address.";
	}
	// Check Message
	if (strlen($contact_message) < 15) {
		$error['message'] = "Please enter your message. It should have at least 15 characters.";
	}
   // Subject
	if ($subject == '') { $subject = "Contact Form Submission"; }


   // Set Message
   $message = "Email from: " . $name . "<br />";
   $message .= "Email address: " . $email . "<br />";
   $message .= "Message: <br />";
   $message .= $contact_message;
   $message .= "<br /> ----- <br /> This email was sent from your site's contact form. <br />";

   // Set From: header
   $from =  $name . " <" . $email . ">";

   // Email Headers
	$headers = "From: " . $from . "\r\n";
	$headers .= "Reply-To: ". $email . "\r\n";
 	$headers .= "MIME-Version: 1.0\r\n";
	$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";


   if (!$error) {

      ini_set("sendmail_from", $siteOwnersEmail); // for windows server
      $mail = mail($siteOwnersEmail, $subject, $message, $headers);

		if ($mail) { echo "OK"; }
      else { echo "Something went wrong. Please try again."; }
		
	} # end if - no validation error

	else {

		$response = (isset($error['name'])) ? $error['name'] . "<br /> \n" : null;
		$response .= (isset($error['email'])) ? $error['email'] . "<br /> \n" : null;
		$response .= (isset($error['message'])) ? $error['message'] . "<br />" : null;
		
		echo $response;

	} # end if - there was a validation error

}

?>

I can’t help you debug your application. This forum is for help with Caddy, and you’ve gotten that part working at this point.

1 Like

Yeah I thought so aswell. Had little hope that it could’ve been something precalibrated by caddy. Sorry for that.
Anyways, thank you very much

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.