Testimonials
What our customers say about Team Password Manager

Full installation of Team Password Manager on Windows Server 2025 with Apache

Current Team Password Manager version: 15.200.312

This tutorial explains how to install Team Password Manager on Windows Server 2025 with the following components: Apache 2.4 (Apache Lounge build), MySQL 8.4 LTS, PHP 8.5 (Thread Safe), SourceGuardian Loader 17 and a TLS/SSL certificate for https, either a commercial one or a free Let's Encrypt one that renews automatically.

A Linux version of this tutorial can be found here: Install on Ubuntu with Apache. We no longer recommend XAMPP for production, as it has been discontinued and ships an outdated PHP.

To follow this tutorial you'll need: a running Windows Server 2025 system (Standard or Datacenter, Desktop Experience, preferably with nothing else installed) that can be reached from the Internet, and an account in the local Administrators group. All commands are run in PowerShell as Administrator (right-click Start > Terminal (Admin)). You'll also need some basic Windows administration knowledge.

Component versions used in this tutorial (always download the latest patch release of each):

Component Version Download
Apache HTTP Server 2.4.69 Win64 VS18 Apache Lounge
Visual C++ Redistributable 2017-2026 x64 (14.51+) Microsoft Learn
MySQL Community Server 8.4 LTS (MSI) dev.mysql.com
PHP 8.5.12 VS17 x64 Thread Safe php.net
SourceGuardian Loader 17.0.0 Windows 64-bit sourceguardian.com
win-acme (only for Let's Encrypt, section 7b) 2.2.9.1 x64 trimmed win-acme.com

Sections:

  1. Basic settings
  2. Visual C++ Redistributable
  3. Apache installation
  4. MySQL installation
  5. PHP installation
  6. Configuring the web server using a virtual host
  7. HTTPS and installing a commercial SSL certificate (or 7b: a free Let's Encrypt certificate)
  8. SourceGuardian Loader installation
  9. Team Password Manager database and user
  10. Team Password Manager files, configuration and installation
  11. Firewall summary and hardening
  12. Updating PHP

We'll use these folders throughout (no spaces in paths, which avoids quoting problems in Apache and PHP):

Folder Contents
C:\Apache24 Apache
C:\PHP PHP
C:\www\tpm.mycompany.com\public_html Team Password Manager files
C:\www\tpm.mycompany.com\logs Apache logs for the site

1. Basic settings

We'll be creating a Team Password Manager installation that will be accessed using this URL: https://tpm.mycompany.com. We'll refer to this URL throughout this document, so adjust it to your own URL each time.

Update the system. Install all pending updates and reboot if asked. From PowerShell you can run sconfig and choose option 6 (Install updates), or use Settings > Windows Update.

Update the hosts file with the hostname and the Team Password Manager URL:

  1. Get the hostname of your server: hostname
  2. Edit C:\Windows\System32\drivers\etc\hosts (open Notepad as Administrator) and add this line: IP_ADDRESS hostname tpm.mycompany.com

If, for instance, the IP address of your server is 1.2.3.4 and your hostname is TPM-SERVER, the line should be:

1.2.3.4 TPM-SERVER tpm.mycompany.com

Set the timezone of your server. Here we set it to UTC:

Set-TimeZone -Id "UTC"

See all the timezones you can use with Get-TimeZone -ListAvailable and check the result with Get-Date.

Turn on time synchronization (NTP) so that the server time is synced over the Internet. This is a must if you plan to use two-factor authentication in Team Password Manager. If the server is joined to an Active Directory domain it already syncs with the domain controllers and you can skip this. Otherwise run:

w32tm /config /manualpeerlist:"time.windows.com,0x9" /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
w32tm /resync
w32tm /query /status

The last command should show Source: time.windows.com and a recent Last Successful Sync Time.

Check the firewall. Windows Defender Firewall is enabled by default on Windows Server 2025, and inbound traffic is blocked unless a rule allows it. Check that it's on for all profiles:

Get-NetFirewallProfile | Format-Table Name, Enabled

All three profiles (Domain, Private, Public) should show True. If not, enable them with Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled True. Outgoing NTP (UDP 123) needs no rule, as outbound traffic is allowed by default. We'll open ports 80 and 443 when we install Apache.

2. Visual C++ Redistributable

Apache, PHP and MySQL for Windows all need the Microsoft Visual C++ runtime. Apache Lounge's VS18 build needs the latest Visual C++ Redistributable for Visual Studio 2017-2026 (x64), version 14.51 or newer. This single package also covers PHP's VS17 build and MySQL.

Go to Microsoft's Latest supported Visual C++ Redistributable downloads page and download the X64 installer (VC_redist.x64.exe), or download it directly from https://aka.ms/vc14/vc_redist.x64.exe, which is the permalink Microsoft gives on that page for the latest x64 version. Run it and reboot if asked.

To check the installed version:

Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" | Select-Object Version

3. Apache 2.4 installation

The Apache Software Foundation only publishes source code, so we'll use the Windows binaries from Apache Lounge, the build referenced by the ASF and by PHP.

3.1 Go to https://www.apachelounge.com/download/ and download the Win64 zip (at the time of writing httpd-2.4.69-261002-Win64-VS18.zip). Before extracting, remove the "downloaded from the Internet" mark:

Unblock-File $env:USERPROFILE\Downloads\httpd-2.4.69-261002-Win64-VS18.zip

3.2 Open the zip and extract the Apache24 folder to C:\, so you end up with C:\Apache24\bin\httpd.exe. If you extract it somewhere else, change the Define SRVROOT line at the top of C:\Apache24\conf\httpd.conf.

3.3 Edit C:\Apache24\conf\httpd.conf and set ServerName (it's commented out by default) to avoid a startup warning:

ServerName tpm.mycompany.com:80

3.4 Check the configuration and install Apache as a Windows service (the service will be called Apache2.4, set to start automatically):

cd C:\Apache24\bin
.\httpd.exe -t
.\httpd.exe -k install
Start-Service Apache2.4

httpd -t should return Syntax OK.

Note on MPMs: on Ubuntu we switch Apache to mpm_prefork. On Windows there's only one MPM, mpm_winnt (a single child process with many threads), which is built in. That's why we'll use the Thread Safe build of PHP.

3.5 Allow http and https (ports 80 and 443) through the firewall:

New-NetFirewallRule -DisplayName "Apache HTTP/HTTPS" -Direction Inbound -Protocol TCP -LocalPort 80,443 -Action Allow -Profile Any

To test that Apache is running, open a browser and type the IP address of your server. You should see the Apache default page with the text It works!. This page is served from C:\Apache24\htdocs.

4. MySQL 8 installation

We'll use MySQL 8.4 LTS as the database. MySQL 8.0 reached end of life in April 2026, so don't install it on a new server. MySQL for Windows is installed with an MSI package and configured afterwards with MySQL Configurator.

4.1 Go to https://dev.mysql.com/downloads/mysql/8.4.html, select Microsoft Windows and download the Windows (x86, 64-bit), MSI Installer (mysql-8.4.x-winx64.msi). You can skip the Oracle account login with "No thanks, just start my download".

4.2 Run the MSI and choose the Typical setup type. The server is installed in C:\Program Files\MySQL\MySQL Server 8.4 and the data and configuration in C:\ProgramData\MySQL\MySQL Server 8.4. At the end, leave Run MySQL Configurator checked.

4.3 In MySQL Configurator use these options:

  • Type and Networking: Config Type Server Computer, TCP/IP enabled on port 3306. Uncheck "Open Windows Firewall ports for network access": Team Password Manager connects locally, so MySQL must not be reachable from outside.
  • Accounts and Roles: set a strong password for root. No other accounts are needed here; we'll create the Team Password Manager user later.
  • Windows Service: configure MySQL as a Windows service (default name MySQL84), start it at system startup and run it as Standard System Account.
  • Server File Permissions: let the Configurator grant full access to the service account and Administrators only.
  • Sample Databases: don't install them.

Click Execute to apply the configuration and Finish.

Unlike on Linux, you don't need to run mysql_secure_installation: MySQL 8.4 is initialized without anonymous users or a test database, and root can only log in from localhost.

4.4 Make MySQL listen only on localhost and disable the X Protocol (port 33060), which Team Password Manager doesn't use. Edit C:\ProgramData\MySQL\MySQL Server 8.4\my.ini with Notepad as Administrator and add these lines in the [mysqld] section:

bind-address=127.0.0.1
mysqlx=OFF

Then restart the service:

Restart-Service MySQL84

4.5 Add the MySQL bin folder to the system PATH so that you can use the mysql client from any prompt:

[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path","Machine") + ";C:\Program Files\MySQL\MySQL Server 8.4\bin", "Machine")

Open a new PowerShell window and test the connection:

mysql -u root -p

Enter the root password. You'll be placed at the MySQL prompt: mysql>. Check the version with SELECT VERSION(); and type exit to return to PowerShell. You can also check that MySQL only listens on localhost with Get-NetTCPConnection -LocalPort 3306 -State Listen (LocalAddress should be 127.0.0.1).

5. PHP 8.5 installation

We'll run PHP as an Apache module (mod_php), which on Windows requires the Thread Safe (TS) build of PHP. Use PHP 8.5.12 or later: 8.5.12 (October 21, 2026) fixes the critical remote code execution vulnerabilities announced that day.

5.1 Go to https://www.php.net/downloads.php?os=windows and download the VS17 x64 Thread Safe zip (at the time of writing php-8.5.12-Win32-vs17-x64.zip). Don't download the "nts" or "Debug Pack" files. Unblock the zip and extract its contents to C:\PHP:

Unblock-File $env:USERPROFILE\Downloads\php-8.5.12-Win32-vs17-x64.zip
Expand-Archive $env:USERPROFILE\Downloads\php-8.5.12-Win32-vs17-x64.zip -DestinationPath C:\PHP

You should now have C:\PHP\php.exe and C:\PHP\php8apache2_4.dll (the Apache module).

5.2 Add C:\PHP to the system PATH. This is needed so that the PHP extensions can find the DLLs they depend on (for instance libssh2.dll for curl or libsasl.dll for LDAP):

[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path","Machine") + ";C:\PHP", "Machine")

Windows services only pick up a new system PATH after a reboot, so reboot the server once you finish this section.

Check the version in a new PowerShell window with php -v. You should see an output similar to this one:

PHP 8.5.12 (cli) (built: Oct 21 2026 13:21:55) (ZTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Zend Engine v4.5.12, Copyright (c) Zend Technologies

"ZTS" confirms that it's the Thread Safe build.

5.3 Create php.ini from the production template:

Copy-Item C:\PHP\php.ini-production C:\PHP\php.ini

Edit C:\PHP\php.ini and set the following values (most of them are already there, commented out with a ; that you need to remove):

extension_dir = "C:/PHP/ext"

extension=curl
extension=gd
extension=ldap
extension=mbstring
extension=mysqli
extension=openssl

session.upload_progress.enabled=Off

date.timezone = "UTC"

Some notes:

  • These are the extensions required by Team Password Manager. Hash, XML, date and zlib are built into PHP for Windows, so they don't need an extension= line. ldap is only needed for LDAP authentication, but it doesn't hurt to have it.
  • Use the same timezone you set in Basic settings (all the PHP timezones can be found here: https://www.php.net/manual/en/timezones.php).
  • Don't install or enable Xdebug: Team Password Manager doesn't run with it.
  • In PHP for Windows sessions are cleaned up by PHP itself (session.gc_probability = 1 in php.ini-production), so no scheduled task is needed for that.

Check that the extensions load without errors with php -m. You should see curl, gd, ldap, mbstring, mysqli and openssl in the list, and no warnings at the top.

5.4 Tell Apache to load PHP. Edit C:\Apache24\conf\httpd.conf and add these lines at the end:

# PHP 8.5
PHPIniDir "C:/PHP"
LoadModule php_module "C:/PHP/php8apache2_4.dll"
<FilesMatch "\.php$">
    SetHandler application/x-httpd-php
</FilesMatch>

In the same file, find the DirectoryIndex line and change it to:

DirectoryIndex index.php index.html

Check the configuration and restart Apache (or reboot now, as explained in 5.2):

C:\Apache24\bin\httpd.exe -t
Restart-Service Apache2.4

5.5 To check that PHP is correctly installed and communicating with Apache:

  1. Create a file called phpinfo.php in C:\Apache24\htdocs with this content: <?php phpinfo();
  2. Open it with the browser pointing at your IP address: http://IP_ADDRESS/phpinfo.php

You should see the phpinfo page. Check that Server API is "Apache 2.0 Handler", Thread Safety is "enabled", Loaded Configuration File is C:\PHP\php.ini, and that there are sections for curl, gd, ldap, mbstring, mysqli and openssl. You can save it to PDF for your records. Afterwards, delete the phpinfo.php file.

6. Configuring the web server to run Team Password Manager using a virtual host

As stated earlier, we're going to access Team Password Manager using https://tpm.mycompany.com. To make this possible, you need to do two things:

  1. Create a new A/AAAA record in your DNS manager so that tpm.mycompany.com resolves to the server's IP address.
  2. Configure a virtual host in Apache to accept tpm.mycompany.com (on port 80 (http) and 443 (https)) and make it serve the Team Password Manager installation.

6.1 DNS. Go to the DNS manager for your domain and create an A record (AAAA if IPv6) for tpm.mycompany.com with the IP address of your server. To test, enter tpm.mycompany.com in the browser and you should see the "It works!" page. Propagation can take a few minutes. If the server is behind a router or NAT, forward ports 80 and 443 to it.

6.2 Create the installation folders:

New-Item -ItemType Directory -Path C:\www\tpm.mycompany.com\public_html, C:\www\tpm.mycompany.com\logs

public_html is where we'll put the Team Password Manager files and logs is where Apache will write the logs for this site.

6.3 Set the permissions. The Apache service runs as Local System (SYSTEM). We'll remove the inherited permissions (which give read access to all local Users) and leave full control only to SYSTEM and Administrators. This is the Windows equivalent of the 770 permissions in the Linux tutorial:

icacls C:\www\tpm.mycompany.com /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"

Check the result with icacls C:\www\tpm.mycompany.com. You should only see these two entries:

C:\www\tpm.mycompany.com NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)

The public_html and logs subfolders inherit these permissions.

6.4 Create the virtual host. Create the file C:\Apache24\conf\extra\tpm.mycompany.com.conf with this content (remember to change tpm.mycompany.com to your own subdomain):

<Directory "C:/www/tpm.mycompany.com/public_html">
    Require all granted
    AllowOverride All
    Options FollowSymLinks
</Directory>
<VirtualHost *:80>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com

    DocumentRoot "C:/www/tpm.mycompany.com/public_html"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined
</VirtualHost>

Some explanations:

  • Apache on Windows uses forward slashes in paths.
  • DocumentRoot tells Apache to serve the files in C:/www/tpm.mycompany.com/public_html for requests to tpm.mycompany.com.
  • ErrorLog and CustomLog create specific logs for this installation. These are web server logs, not the internal log of Team Password Manager (which can be found in the top menu of the software, "Log" option).
  • Require all granted: all IP addresses can access the site. AllowOverride All: allows .htaccess files to override the Apache configuration. Options FollowSymLinks without Indexes disables directory listing.

6.5 Enable the site. There's no a2ensite on Windows; instead, add this line at the end of C:\Apache24\conf\httpd.conf:

Include conf/extra/tpm.mycompany.com.conf

Since this is the only virtual host, Apache uses it as the default for any request on port 80, so the "It works!" page in C:\Apache24\htdocs is no longer served (the equivalent of disabling the default site). Delete the contents of C:\Apache24\htdocs if you want to be thorough.

Check the configuration and restart Apache:

C:\Apache24\bin\httpd.exe -t
Restart-Service Apache2.4

To test, enter http://tpm.mycompany.com/ in your browser. You should see a Forbidden message, as we haven't put any files in public_html yet (and directory listing is disabled). Check the logs folder and you'll see the two log files: access.log and error.log.

7. HTTPS and installing an SSL Certificate from a Commercial Certificate Authority

Up to this point the server accepts requests to http://tpm.mycompany.com. In this section we'll install a commercial SSL certificate and configure Apache to answer on https://tpm.mycompany.com, so that the communication between the browser and the server is encrypted.

Commercial certificate or Let's Encrypt? This section uses a commercial certificate. If you prefer a free Let's Encrypt certificate that renews automatically, skip this section and follow section 7b instead.

7.1 Create the private key and CSR. Apache Lounge includes openssl.exe, so we'll use it. Create a protected folder for the certificate files, accessible only by SYSTEM and Administrators:

New-Item -ItemType Directory -Path C:\Apache24\conf\ssl
icacls C:\Apache24\conf\ssl /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"
cd C:\Apache24\conf\ssl
$env:OPENSSL_CONF = "C:\Apache24\conf\openssl.cnf"
C:\Apache24\bin\openssl.exe req -new -newkey rsa:2048 -nodes -keyout tpm.mycompany.com.key -out tpm.mycompany.com.csr

You'll be asked for some information that will be included in the certificate request. It's especially important that you enter the Common Name correctly: it's the hostname you'll use to access Team Password Manager, in our case tpm.mycompany.com:

Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:Barcelona
Locality Name (eg, city) []:My city
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My company
Organizational Unit Name (eg, section) []:Security
Common Name (e.g. server FQDN or YOUR name) []:tpm.mycompany.com
Email Address []:info@mycompany.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

This creates two files: tpm.mycompany.com.key (the private key, which you must protect and not disclose to anyone) and tpm.mycompany.com.csr (the CSR, which you'll paste into the CA's enrollment form).

7.2 Get the certificate. Purchase the certificate from a Commercial Certificate Authority. We've used Namecheap (PositiveSSL) but you can use any CA. Paste the contents of the CSR (everything from -----BEGIN CERTIFICATE REQUEST to END CERTIFICATE REQUEST-----, view it with Get-Content tpm.mycompany.com.csr) in the CA's form and validate your domain using one of the methods the CA offers.

The CA will give you two files: tpm_mycompany_com.crt and tpm_mycompany_com.ca-bundle. Copy them to C:\Apache24\conf\ssl. They'll inherit the folder's restricted permissions. The result should look like this (Get-ChildItem C:\Apache24\conf\ssl):

tpm.mycompany.com.csr
tpm.mycompany.com.key
tpm_mycompany_com.ca-bundle
tpm_mycompany_com.crt

Note: we advise you to store all the certificate files outside the server, in a backup location.

7.3 Enable the SSL and headers modules. Edit C:\Apache24\conf\httpd.conf and uncomment (remove the # at the beginning of) these three lines:

LoadModule headers_module modules/mod_headers.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so

We don't include conf/extra/httpd-ssl.conf; the SSL configuration goes in our own virtual host file.

7.4 Configure https. Edit C:\Apache24\conf\extra\tpm.mycompany.com.conf to a) redirect http to https and b) configure https. It should look like this (as always, replacing tpm.mycompany.com with your own subdomain). The new lines are Listen 443, the SSL global settings, the Redirect and the whole <VirtualHost *:443> block:

Listen 443

SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder off
SSLSessionCache "shmcb:C:/Apache24/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300

<Directory "C:/www/tpm.mycompany.com/public_html">
    Require all granted
    AllowOverride All
    Options FollowSymLinks
</Directory>
<VirtualHost *:80>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com
    DocumentRoot "C:/www/tpm.mycompany.com/public_html"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined
    Redirect 301 / https://tpm.mycompany.com/
</VirtualHost>
<VirtualHost *:443>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com
    DocumentRoot "C:/www/tpm.mycompany.com/public_html"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined

    SSLEngine on
    SSLCertificateFile "C:/Apache24/conf/ssl/tpm_mycompany_com.crt"
    SSLCertificateKeyFile "C:/Apache24/conf/ssl/tpm.mycompany.com.key"
    SSLCertificateChainFile "C:/Apache24/conf/ssl/tpm_mycompany_com.ca-bundle"

    # HSTS (mod_headers is required) (15768000 seconds = 6 months)
    Header always set Strict-Transport-Security "max-age=15768000"
</VirtualHost>

Some explanations:

  • Listen 443 makes Apache listen on the https port (on Ubuntu this is done by the ssl module configuration).
  • SSLProtocol only allows TLS 1.2 and 1.3, disabling older insecure protocols.
  • The Redirect in the <VirtualHost *:80> section sends all http requests to https, so even if a user goes to http://tpm.mycompany.com she'll be redirected to https://tpm.mycompany.com.
  • The <VirtualHost *:443> section answers requests to https://tpm.mycompany.com. It's the same as the http section plus the SSL directives.

Check the configuration and restart Apache:

C:\Apache24\bin\httpd.exe -t
Restart-Service Apache2.4

If you go to http://tpm.mycompany.com you should now be redirected to https://tpm.mycompany.com and see the same Forbidden message as before, but this time with the secure connection "lock" icon in the browser. Click it to see the certificate information.

7b. HTTPS with a free Let's Encrypt certificate (alternative to section 7)

This section is an alternative to section 7: follow one or the other. Here we'll get a free certificate from Let's Encrypt and renew it automatically with win-acme, the most widely used Let's Encrypt client for Windows.

Before you start, check the requirements:

  • tpm.mycompany.com must resolve to your server in public DNS (section 6.1).
  • Port 80 must be reachable from the Internet, now and at every renewal. Let's Encrypt checks that you control the domain by downloading a file from http://tpm.mycompany.com/.well-known/acme-challenge/ (HTTP-01 validation). The firewall rule from section 3.5 already allows it, so don't close port 80 later.

If your server isn't reachable from the Internet (an internal-only installation), this method won't work. Use a commercial certificate (section 7), a certificate from your internal CA, or win-acme's DNS validation, which needs API access to your DNS provider and isn't covered here.

Let's Encrypt certificates are valid for 90 days (Let's Encrypt plans to reduce this to 45 days by 2028). win-acme renews them automatically with a scheduled task, so once it's set up there's nothing else to do.

7b.1 Create the folders. We'll use a separate folder for the validation files, so that Let's Encrypt never touches the Team Password Manager files, and a protected folder for the certificate:

New-Item -ItemType Directory -Force -Path C:\www\acme\.well-known\acme-challenge, C:\Apache24\conf\ssl
icacls C:\www\acme /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"
icacls C:\Apache24\conf\ssl /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"

7b.2 Serve the validation folder on port 80. Edit C:\Apache24\conf\extra\tpm.mycompany.com.conf and add the <Directory "C:/www/acme"> block and the Alias line, so that it looks like this:

<Directory "C:/www/tpm.mycompany.com/public_html">
    Require all granted
    AllowOverride All
    Options FollowSymLinks
</Directory>
<Directory "C:/www/acme">
    Require all granted
    AllowOverride None
    Options None
</Directory>
<VirtualHost *:80>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com

    DocumentRoot "C:/www/tpm.mycompany.com/public_html"
    Alias /.well-known/acme-challenge/ "C:/www/acme/.well-known/acme-challenge/"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined
</VirtualHost>

Don't add the https virtual host yet: Apache wouldn't start, because the certificate files don't exist yet. Check the configuration and restart Apache:

C:\Apache24\bin\httpd.exe -t
Restart-Service Apache2.4

To test it, create a test file with Set-Content C:\www\acme\.well-known\acme-challenge\test.txt "ok" and open http://tpm.mycompany.com/.well-known/acme-challenge/test.txt from outside your network (for instance, from your phone using mobile data). You should see "ok". Then delete the file.

7b.3 Create the Apache restart script. win-acme will run this script after getting or renewing the certificate, so that Apache loads the new one. It only restarts Apache if the configuration is valid. Create C:\Apache24\bin\restart-apache.ps1 with this content:

& C:\Apache24\bin\httpd.exe -t 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Error "Apache configuration test failed; Apache was not restarted."
    exit 1
}
Restart-Service Apache2.4

7b.4 Install win-acme. Go to https://www.win-acme.com/ and download the x64 trimmed zip, the recommended download (version 2.2.9.1 at the time of writing). Extract it to C:\Program Files\win-acme, a folder only Administrators can modify. This matters because the renewal task runs as SYSTEM.

$zip = Get-ChildItem $env:USERPROFILE\Downloads\win-acme*x64.trimmed.zip | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Unblock-File $zip.FullName
Expand-Archive $zip.FullName -DestinationPath "C:\Program Files\win-acme"

7b.5 Request the certificate. Run win-acme with these options (change the hostname and e-mail address to yours):

cd "C:\Program Files\win-acme"
.\wacs.exe --source manual --host tpm.mycompany.com `
  --validation filesystem --webroot "C:\www\acme" `
  --store pemfiles --pemfilespath "C:\Apache24\conf\ssl" `
  --installation script --script "C:\Apache24\bin\restart-apache.ps1" `
  --accepttos --emailaddress info@mycompany.com

What each option does:

  • --source manual --host: the hostname the certificate is for.
  • --validation filesystem --webroot: writes the validation file to C:\www\acme\.well-known\acme-challenge\, which Apache serves thanks to the Alias.
  • --store pemfiles --pemfilespath: saves the certificate as .pem files, the format Apache uses, in C:\Apache24\conf\ssl.
  • --installation script --script: runs the restart script after each new certificate.
  • --accepttos --emailaddress: accepts the Let's Encrypt terms of service and registers your e-mail address with the account.

win-acme validates the domain, downloads the certificate, runs the restart script and creates the renewal scheduled task. Check the certificate files with Get-ChildItem C:\Apache24\conf\ssl:

tpm.mycompany.com-chain-only.pem
tpm.mycompany.com-chain.pem
tpm.mycompany.com-crt.pem
tpm.mycompany.com-key.pem

If validation fails, check that the test in 7b.2 works from outside your network. Let's Encrypt limits the number of failed validations per hour, so if you want to experiment, add --test to the command: it uses Let's Encrypt's staging server, whose certificates browsers don't trust.

7b.6 Configure https in Apache. Edit C:\Apache24\conf\httpd.conf and uncomment (remove the # at the beginning of) these three lines:

LoadModule headers_module modules/mod_headers.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so

Then edit C:\Apache24\conf\extra\tpm.mycompany.com.conf to redirect http to https and configure https. It should look like this:

Listen 443

SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder off
SSLSessionCache "shmcb:C:/Apache24/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300

<Directory "C:/www/tpm.mycompany.com/public_html">
    Require all granted
    AllowOverride All
    Options FollowSymLinks
</Directory>
<Directory "C:/www/acme">
    Require all granted
    AllowOverride None
    Options None
</Directory>
<VirtualHost *:80>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com

    DocumentRoot "C:/www/tpm.mycompany.com/public_html"
    Alias /.well-known/acme-challenge/ "C:/www/acme/.well-known/acme-challenge/"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined
    RedirectMatch 301 "^/(?!\.well-known/acme-challenge/)(.*)$" "https://tpm.mycompany.com/$1"
</VirtualHost>
<VirtualHost *:443>
    ServerName tpm.mycompany.com
    ServerAdmin info@mycompany.com
    DocumentRoot "C:/www/tpm.mycompany.com/public_html"

    ErrorLog "C:/www/tpm.mycompany.com/logs/error.log"
    CustomLog "C:/www/tpm.mycompany.com/logs/access.log" combined

    SSLEngine on
    SSLCertificateFile "C:/Apache24/conf/ssl/tpm.mycompany.com-chain.pem"
    SSLCertificateKeyFile "C:/Apache24/conf/ssl/tpm.mycompany.com-key.pem"

    # HSTS (mod_headers is required) (15768000 seconds = 6 months)
    Header always set Strict-Transport-Security "max-age=15768000"
</VirtualHost>

The differences with the commercial certificate configuration in section 7.4:

  • RedirectMatch sends every http request to https, except the validation files, so renewals keep working over plain http.
  • SSLCertificateFile points to the -chain.pem file, which contains the certificate and the intermediate certificates, so SSLCertificateChainFile isn't needed.

Check the configuration and restart Apache:

C:\Apache24\bin\httpd.exe -t
Restart-Service Apache2.4

If you go to http://tpm.mycompany.com you should now be redirected to https://tpm.mycompany.com and see the Forbidden message, with the secure connection "lock" icon in the browser. The certificate information shows Let's Encrypt as the issuer.

7b.7 Automatic renewal. win-acme created a scheduled task that runs every day as SYSTEM, renews the certificate when it's due and runs the restart script. Check that it exists and see its last and next run:

Get-ScheduledTask -TaskName "win-acme*" | Get-ScheduledTaskInfo | Format-List TaskName, LastRunTime, LastTaskResult, NextRunTime

LastTaskResult should be 0 after the task has run at least once. To see the renewals win-acme manages and their dates:

& "C:\Program Files\win-acme\wacs.exe" --list

To test the whole renewal process now (it gets a new certificate and restarts Apache):

& "C:\Program Files\win-acme\wacs.exe" --renew --force

Don't run --force regularly: Let's Encrypt limits how many certificates you can get for the same hostname in a given period.

Monitor the expiry date. Let's Encrypt stopped sending expiry warning e-mails in 2025, so if renewals fail nobody will warn you. Check the scheduled task result from time to time, or use an external service that monitors certificate expiry.

8. SourceGuardian Loader installation

Team Password Manager is a PHP web application encoded and secured using an encoder (SourceGuardian), and a loader is required to execute it. You need at least version 17.0.0 of the SourceGuardian Loader, which supports PHP 8.5.

8.1 Go to https://www.sourceguardian.com/loaders.html, accept the terms and download the Windows 64-bit zip package: loaders.windows-x86_64.zip.

8.2 Unzip it and copy the loader for PHP 8.5 Thread Safe, ixed.8.5ts.win, into C:\PHP\ext. Make sure you take the ts file: the one without "ts" is for the Non Thread Safe build and won't load in Apache.

8.3 Edit C:\PHP\php.ini and put this line just below [PHP]:

extension=ixed.8.5ts.win

8.4 Check it from the command line with php -v. The output should now include a line similar to with SourceGuardian v17.0.0. Then restart Apache:

Restart-Service Apache2.4

To test it in Apache, create a file called phpinfo.php in C:\www\tpm.mycompany.com\public_html with this content: <?php phpinfo(); and open https://tpm.mycompany.com/phpinfo.php. Check that there's a SourceGuardian section showing loader version 17 or higher. Then delete the phpinfo.php file.

If you're not sure which loader file to use, the SourceGuardian Loader Assistant can tell you from your phpinfo output.

9. Team Password Manager database and user

In this section we'll create the database that will store the Team Password Manager information and a user to access it from the software. We're going to use these values; we advise you to use your own:

  • Database: tpm_database
  • User: tpm_user
  • Password: tpmPass%2025

9.1 Enter the mysql prompt as root (you'll be asked for the root password you set in the MySQL Configurator):

mysql -u root -p

9.2 Create the database:

mysql> CREATE DATABASE `tpm_database` CHARACTER SET utf8 COLLATE utf8_unicode_ci;

MySQL 8.4 shows a warning saying that utf8 is an alias for utf8mb3. It's expected and you can ignore it.

9.3 Create the user:

mysql> CREATE USER 'tpm_user'@'127.0.0.1' IDENTIFIED BY 'tpmPass%2025';

Note that we create the user for 127.0.0.1 instead of % as in the Linux tutorial. On Windows PHP connects to MySQL over TCP (there's no Unix socket), and since MySQL only listens on 127.0.0.1, this is the only host the user needs.

9.4 Grant all the privileges to the user on the database:

mysql> GRANT ALL ON tpm_database.* TO 'tpm_user'@'127.0.0.1';
mysql> FLUSH PRIVILEGES;

Type exit to return to PowerShell.

10. Team Password Manager files, configuration file and installation script

10.1 Files. Download the Team Password Manager software from https://teampasswordmanager.com/download/, unblock the zip (Unblock-File) and extract all its files and folders into C:\www\tpm.mycompany.com\public_html. The files inherit the permissions we set on the site folder (SYSTEM and Administrators only), so there's nothing else to do. You should have something like this (Get-ChildItem C:\www\tpm.mycompany.com\public_html):

d----   css
d----   import
d----   system
d----   uploads
d----   wmm
-a---   config.php
-a---   eula.txt
-a---   folder.php
-a---   index.php
-a---   install.txt
-a---   robots.txt
-a---   upgrade.txt

10.2 Configuration file (config.php). Edit config.php (Notepad is fine) and enter the database, user and password configured in the previous section:

// MySQL Database server
define('CONFIG_HOSTNAME', '127.0.0.1');

// User that accesses the database server, that should have all privileges on the database CONFIG_DATABASE
define('CONFIG_USERNAME', 'tpm_user');

// User password
define('CONFIG_PASSWORD', 'tpmPass%2025');

// Database for Team Password Manager. You must manually create it before installing Team Password Manager
define('CONFIG_DATABASE', 'tpm_database');

Use 127.0.0.1 rather than localhost: it matches the MySQL user we created and avoids Windows resolving localhost to the IPv6 address ::1, which MySQL isn't listening on.

10.3 Installation script. Open your browser and enter this in the address bar:

https://tpm.mycompany.com/index.php/install

You should see the Team Password Manager installation screen. Just enter the values and complete the installation.

If everything went well Team Password Manager is now installed. Congratulations!!

11. Firewall summary and hardening

With this setup only ports 80 and 443 are open to the outside; MySQL is reachable only from the server itself.

Port Service Inbound access
80/TCP Apache (http, redirects to https) Open (rule "Apache HTTP/HTTPS"); also needed for Let's Encrypt renewals
443/TCP Apache (https) Open (rule "Apache HTTP/HTTPS")
3306/TCP MySQL Closed: no firewall rule, bound to 127.0.0.1
33060/TCP MySQL X Protocol Disabled (mysqlx=OFF)
3389/TCP Remote Desktop Only if you enabled RDP; restrict it (see below)

Check the rules. List the enabled inbound rules and make sure nothing opens MySQL:

Get-NetFirewallRule -Direction Inbound -Enabled True -Action Allow | Format-Table DisplayName, Profile
Get-NetFirewallRule | Where-Object DisplayName -like "*MySQL*"

If the second command returns a rule (for instance because the Firewall checkbox was left on in MySQL Configurator), remove it with Remove-NetFirewallRule -DisplayName "<rule name>".

Restrict Remote Desktop. If you manage the server through RDP, limit it to your office or VPN addresses (replace 203.0.113.10 with yours):

Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress 203.0.113.10

Hide version information. Add these lines at the end of C:\Apache24\conf\httpd.conf and restart Apache:

ServerTokens Prod
ServerSignature Off

(expose_php is already Off in php.ini-production.)

Rotate the logs. Apache on Windows doesn't rotate logs by itself. If you want daily log files, replace the CustomLog lines in both virtual hosts with:

CustomLog "|C:/Apache24/bin/rotatelogs.exe -l C:/www/tpm.mycompany.com/logs/access.%Y-%m-%d.log 86400" combined

Keep the components updated. Windows Update doesn't update Apache, PHP or MySQL. Follow the release announcements of Apache Lounge, PHP for Windows and MySQL 8.4, and apply security releases promptly. To update PHP, see the next section. MySQL 8.4 updates are installed by running the newer MSI.

12. Updating PHP

PHP for Windows doesn't update itself and Windows Update doesn't touch it, so you update it by replacing the C:\PHP folder and keeping your configuration. Always keep the old folder until you've checked that the new version works, so you can go back in a minute.

12.1 Patch release within PHP 8.5 (for example 8.5.12 to 8.5.13)

Patch releases keep the same DLL names and the same SourceGuardian loader, so you only need to swap the files.

1. Download the new VS17 x64 Thread Safe zip from https://www.php.net/downloads.php?os=windows.

2. In PowerShell as Administrator, set the old and new versions and run these commands:

$old = "8.5.12"
$new = "8.5.13"

Stop-Service Apache2.4
Rename-Item C:\PHP "C:\PHP-$old"
Unblock-File "$env:USERPROFILE\Downloads\php-$new-Win32-vs17-x64.zip"
Expand-Archive "$env:USERPROFILE\Downloads\php-$new-Win32-vs17-x64.zip" -DestinationPath C:\PHP
Copy-Item "C:\PHP-$old\php.ini" C:\PHP\
Copy-Item "C:\PHP-$old\ext\ixed.8.5ts.win" C:\PHP\ext\
php -v
Start-Service Apache2.4

If Rename-Item fails because a file is in use, check that Apache is stopped and close any other window running php.

3. Check the update:

  • php -v shows the new version and the with SourceGuardian line.
  • A temporary phpinfo.php (as in section 8) shows the new version, Thread Safety enabled and the SourceGuardian section. Delete it afterwards.
  • You can sign in to Team Password Manager and open a few passwords.

4. Once everything works, delete the old folder: Remove-Item C:\PHP-$old -Recurse.

To roll back, stop Apache, delete C:\PHP, rename the old folder back and start Apache:

Stop-Service Apache2.4
Remove-Item C:\PHP -Recurse
Rename-Item "C:\PHP-$old" C:\PHP
Start-Service Apache2.4

12.2 New minor version (for example PHP 8.5 to 8.6)

Follow the same steps, with these differences:

  • Check compatibility first. Make sure the Team Password Manager requirements list the new PHP version, and update Team Password Manager first if needed.
  • Visual C++ runtime. Check which Visual Studio version the new build uses (VS17, VS18...) on the PHP download page, and install the latest Visual C++ Redistributable if needed (section 2).
  • SourceGuardian loader. Download the latest loaders and copy the one for the new version (for PHP 8.6, ixed.8.6ts.win) to C:\PHP\ext instead of copying the old one. Update the extension=ixed... line in php.ini.
  • php.ini. Instead of copying the old php.ini, create a new one from the new php.ini-production and apply the settings from sections 5.3 and 8.3 again, since defaults can change between versions.
  • Apache. The PHPIniDir and LoadModule php_module "C:/PHP/php8apache2_4.dll" lines stay the same for any PHP 8.x version.

Document changelog

Oct 26, 2026: Document created
Questions or Problems? Please contact our support department