| Tested on | File server: RHEL 10.2 at 192.168.56.116 (vm1.lab.example)Client: RHEL 10.2 at 192.168.56.220 (vm2.lab.example) |
|---|---|
| Package | httpd 2.4.63mod_ssl 2.4.63httpd-tools 2.4.63 (htpasswd)curl 8.12.1openssl 3.5.5 |
| Applies to | RHEL, Rocky Linux, AlmaLinux, and Fedora (httpd, firewalld, SELinux). Client curl commands work on any Linux host with curl installed. |
| Privilege | root or sudo on the RHEL-family HTTPS server; on the client, any user with network access once the hostname resolves (copy the PEM to your home directory; adding a hosts entry or system trust store requires sudo) |
| Scope | RHEL-family server setup: Apache with TLS and WebDAV outside DocumentRoot, HTTP basic auth before starting httpd, verified HTTPS with curl --cacert, upload with curl -T, download with curl -o. Does not cover Debian or Ubuntu apache2 setup, mutual TLS client certificates, or object storage APIs. |
| Related guides | Copy files between Linux servers curl command cheat sheet wget command systemctl command Open firewall port Linux |
You need two hosts: an RHEL-family Apache file server on vm1.lab.example
and a client on vm2.lab.example. Server-side steps use dnf, httpd,
firewalld, and SELinux. The client curl commands work on any Linux host
with curl installed. The server terminates TLS, accepts WebDAV PUT on
/web/, and requires a username and password before httpd starts listening.
The client verifies the server certificate with --cacert, then uploads and
downloads with curl. If you already transfer files with
scp or rsync over SSH,
use this pattern when port 443 and a web endpoint are the required path.
Quick answer
| Goal | Command (run on the client) |
|---|---|
| Upload with verified TLS and auth | curl --cacert ~/vm1.lab.example.crt -u USER -T /path/to/local.file https://vm1.lab.example/web/local.file |
| Download with verified TLS and auth | curl --cacert ~/vm1.lab.example.crt -u USER -o /path/to/save https://vm1.lab.example/web/remote.file |
| Public CA certificate (no extra TLS flag) | curl -u USER -T ... https://SERVER/web/... when the system CA store already trusts the server |
| Lab-only: skip TLS verification | curl -k -u USER -T ... — encrypts traffic but does not verify the server |
Pass only the username to -u; curl prompts for the password instead of
reading it from the command line. Use the hostname that matches the
certificate (vm1.lab.example here), not the bare IP, when you pass
--cacert.
How the HTTPS file transfer works
Three layers stack on top of each other:
- Transport — TLS on port 443 encrypts the connection.
curlmust trust the server certificate (--cacert, system CA store, or a public CA) and the URL hostname must match the certificate name. - Protocol —
curl -Tsends an HTTP PUT request. Apache needs WebDAV (Dav On) on/web/so PUT creates or replaces files on disk. - Access control — HTTP basic authentication limits who can read or
write
/web/. Pass-u usernameand enter the password whencurlprompts.
Store WebDAV files outside Apache's normal DocumentRoot so /web/ is the
only intended HTTP entry point. Files are not also reachable under
https://SERVER/secret/ or another default site path.
| TLS approach | curl flags | Typical use |
|---|---|---|
| Public CA | (none — default) | Production sites with Let's Encrypt or another public CA |
| Private CA or self-signed server | --cacert /path/to/ca-or-trusted-cert.pem |
Internal lab, corporate CA, or a self-signed server certificate copied to the client |
| System trust store | Install cert, then plain curl |
Many scripts on one host calling the same internal server |
| Skip verification | -k |
Isolated debugging only |
Mutual TLS (--cert and --key on the client) is a separate pattern used when
the server requires a client certificate during the TLS handshake. This setup
uses TLS verification plus HTTP basic auth instead.
Install Apache and WebDAV
On RHEL, Rocky Linux, AlmaLinux, or Fedora, install httpd, mod_ssl,
httpd-tools (for htpasswd), openssl, and
policycoreutils-python-utils (needed for semanage on SELinux systems).
WebDAV modules ship with httpd and load from
/etc/httpd/conf.modules.d/00-dav.conf.
dnf install -y httpd mod_ssl httpd-tools openssl policycoreutils-python-utilsConfirm mod_ssl and the DAV modules are loaded:
httpd -M | grep -E 'ssl_module|dav_module|dav_fs_module'Sample output:
dav_module (shared)
dav_fs_module (shared)
ssl_module (shared)dav_module provides WebDAV support, while dav_fs_module lets WebDAV
store files on the local filesystem.
Filesystem WebDAV also needs a lock database. Create it and give apache
ownership:
mkdir -p /var/lib/dav
chown apache:apache /var/lib/davDavLockDB belongs at server configuration level, not inside <Location>.
Create the WebDAV directory
Put upload data outside /var/www/html so it is not also published as part
of the default website tree:
mkdir -p /var/www/webdav
echo 'download-me' > /var/www/webdav/sample.txt
chown apache:apache /var/www/webdav
chmod 775 /var/www/webdavOn RHEL with SELinux enforcing, label the directory so httpd can write
uploaded files:
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/webdav(/.*)?"
restorecon -Rv /var/www/webdavIf semanage reports the rule already exists, run only restorecon.
Create the TLS certificate
Generate a key and certificate for the hostname clients use in the URL.
This example uses vm1.lab.example on the file server:
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-keyout /etc/pki/tls/certs/vm1.lab.example.key \
-out /etc/pki/tls/certs/vm1.lab.example.crt \
-subj "/CN=vm1.lab.example" \
-addext "subjectAltName=DNS:vm1.lab.example"
chmod 600 /etc/pki/tls/certs/vm1.lab.example.keyThe certificate contains vm1.lab.example as a DNS name, so curl must use
that hostname in the HTTPS URL.
Point Apache at those files. On RHEL, edit the two certificate directives
in /etc/httpd/conf.d/ssl.conf:
SSLCertificateFile /etc/pki/tls/certs/vm1.lab.example.crt
SSLCertificateKeyFile /etc/pki/tls/certs/vm1.lab.example.keyReplace the default localhost.crt paths if they are still referenced
there. Do not start httpd yet; create the WebDAV user and authenticated
configuration first.
Create the WebDAV user
Create a password file and user filetransfer on the file server:
htpasswd -c /etc/httpd/webdav.htpasswd filetransferhtpasswd prompts for the password twice instead of reading it from the
command line:
New password:
Re-type new password:
Adding password for user filetransferRestrict permissions so only root and apache can read the file:
chmod 640 /etc/httpd/webdav.htpasswd
chown root:apache /etc/httpd/webdav.htpasswdConfigure authenticated WebDAV
Map /web/ to the directory outside DocumentRoot and require basic auth
before you start Apache:
cat > /etc/httpd/conf.d/file-transfer-webdav.conf <<'EOF'
DavLockDB /var/lib/dav/DavLock
Alias /web/ /var/www/webdav/
<Directory /var/www/webdav>
Options Indexes
AllowOverride None
AuthType Basic
AuthName "HTTPS file transfer"
AuthUserFile /etc/httpd/webdav.htpasswd
Require valid-user
</Directory>
<Location /web/>
Dav On
</Location>
EOFDav On is what allows curl -T (HTTP PUT). Require valid-user means
no unauthenticated read or write access to /web/.
Start Apache
Validate the full configuration:
apachectl configtestSample output:
Syntax OKEnable and start the service:
systemctl enable --now httpdOpen HTTPS in the firewall when firewalld is active:
firewall-cmd --permanent --add-service=https
firewall-cmd --reloadConfirm httpd is listening on 443:
ss -tlnp | grep ':443'Sample output:
LISTEN 0 511 *:443 *:* users:(("httpd",pid=18011,fd=6))Trust the certificate on the client
--cacert tells curl which PEM file to use when verifying the server.
Copy that file to the client through a channel you already trust, such as
your configuration management system, an internal PKI portal, or SSH if you
have it. Do not download the trust material over an unverified HTTPS
connection; that would undermine the bootstrap step.
If SSH access to the file server is available, copy the certificate into your home directory on the client:
scp admin@192.168.56.116:/etc/pki/tls/certs/vm1.lab.example.crt ~/vm1.lab.example.crtThe account must be able to read the PEM file on the server, or an administrator must place a copy in a path you can reach.
If DNS does not already resolve vm1.lab.example, add a hosts entry once.
That step requires sudo on the client:
grep -q 'vm1.lab.example' /etc/hosts || echo '192.168.56.116 vm1.lab.example' | sudo tee -a /etc/hostsTest TLS verification with a read-only GET before you upload anything.
Without a trusted certificate, curl rejects the self-signed server cert:
curl https://vm1.lab.example/web/sample.txtSample output:
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it.Point curl at the copied certificate and supply credentials:
curl --cacert ~/vm1.lab.example.crt -u filetransfer https://vm1.lab.example/web/sample.txtEnter the WebDAV password when prompted. Sample output:
download-meThat confirms both TLS verification and basic authentication work before you run an upload.
A request with --cacert but without -u should return 401:
curl -sS -o /dev/null -w '%{http_code}\n' --cacert ~/vm1.lab.example.crt https://vm1.lab.example/web/sample.txtSample output:
401When many scripts on one host call the same internal server, you can install
the certificate in the system trust store so plain curl works for every
user. That requires root or sudo on the client. On RHEL and Fedora, as root:
cp ~/vm1.lab.example.crt /etc/pki/ca-trust/source/anchors/vm1.lab.example.crtRebuild the bundle:
update-ca-trust extractAfter that, curl against https://vm1.lab.example/... verifies the server
without --cacert on RHEL and Fedora clients.
Upload a file securely with curl
On the client, create a local file and upload it with verified TLS and basic auth:
echo 'upload-from-client' > /tmp/client_upload.txt
curl --cacert ~/vm1.lab.example.crt -u filetransfer -T /tmp/client_upload.txt https://vm1.lab.example/web/client_upload.txt-T (--upload-file) sends the file with HTTP PUT. --cacert verifies
the server certificate. -u filetransfer makes curl prompt with
Enter host password for user 'filetransfer': instead of putting the
password on the command line, where other users could see it in a process
listing.
Sample output after you enter the password:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>201 Created</title>
</head><body>
<h1>Created</h1>
<p>Resource /web/client_upload.txt has been created.</p>
</body></html>Because client_upload.txt did not already exist, 201 Created means
Apache created the new resource successfully. Replacing an existing file
may return a different successful status such as 204 No Content.
On the file server, confirm the uploaded content:
cat /var/www/webdav/client_upload.txtSample output:
upload-from-clientSeeing the expected content confirms that the file uploaded successfully.
Download a file securely with curl
Download the sample file with the same certificate trust and credentials:
curl --cacert ~/vm1.lab.example.crt -u filetransfer -o /tmp/downloaded.txt https://vm1.lab.example/web/sample.txt-o writes the response body to /tmp/downloaded.txt instead of printing
it to the terminal. Enter the WebDAV password when curl prompts for it.
Check the saved file:
cat /tmp/downloaded.txtSample output:
download-meFor more curl flags and HTTP methods, see the
curl command cheat sheet.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
curl: (7) Failed to connect |
Firewall or httpd not listening on 443 |
Run systemctl status httpd; open https in firewalld; verify with ss -tlnp | grep 443 |
401 Unauthorized |
Missing or wrong credentials | Pass -u filetransfer and enter the password at the prompt; confirm the user exists in webdav.htpasswd |
SSL certificate problem: self-signed certificate |
Client does not trust the server cert | Copy the PEM to ~/ and use --cacert, or install it in the system trust store as root |
certificate subject name ... does not match target hostname |
URL uses an IP but the cert names a hostname | Use https://vm1.lab.example/... or reissue the cert with the IP in SAN |
500 Internal Server Error on PUT |
Directory not writable by apache, missing DavLockDB, or SELinux block |
Set DavLockDB and chown apache:apache /var/lib/dav; chown apache:apache /var/www/webdav; apply httpd_sys_rw_content_t with semanage and restorecon |
405 Method Not Allowed |
WebDAV not enabled on /web/ |
Confirm Dav On inside <Location /web/> and restart httpd |
| Files reachable at two URLs | WebDAV data stored under DocumentRoot | Move uploads to a directory outside /var/www/html and map only /web/ with Alias |
CONNECT tunnel failed or unexpected proxy errors |
https_proxy set in the shell |
Run curl --noproxy '*' ... or unset proxy variables for direct HTTPS to the file server |
Upload works only with -k |
TLS verification skipped but hostname or trust still wrong without it | Fix --cacert path and use the certificate hostname in the URL |
References
- curl TLS options — everything curl
- curl client certificates — everything curl
- curl(1)
- Apache mod_auth_basic documentation
- httpd SSL/TLS — Red Hat Documentation
- Apache mod_ssl documentation
- Apache mod_dav documentation
Summary
Secure HTTPS file transfer stacks TLS encryption, certificate verification, and
access control on an RHEL-family file server. You install Apache with
mod_ssl and WebDAV, store uploads in /var/www/webdav outside
DocumentRoot, map only /web/ with basic auth configured before httpd
starts, and copy the server PEM to ~/vm1.lab.example.crt on the client
through a trusted channel for curl --cacert.
Uploads use curl -T with -u username; downloads use curl -o with the
same flags. Enter the password when prompted rather than on the command line.
Both commands target https://vm1.lab.example so the hostname matches the
certificate. Test TLS with a read-only GET before you upload. A 401 response
means credentials are missing; an SSL error usually means --cacert points at
the wrong file or the URL uses an IP instead of the certificate hostname.
On a host with a public CA certificate, plain curl is enough for TLS.
Internal servers need --cacert with a CA or trusted server PEM, or a private
CA in the system trust store. Reserve -k for lab debugging. For day-to-day
server-to-server copies without a web front door, SSH-based tools remain
simpler; use this pattern when HTTPS on port 443 is the required transport.

