前不久接手一台 Windows Server 2016 的服务器,要在上面跑一套 PHP 站点,数据库用 MySQL。本来以为照着网上的教程“下一步下一步”就能搞定,结果光版本匹配就折腾了一整天——PHP 下载线程安全版还是非线程安全版、VC 运行库装了没、IIS 的 FastCGI 映射该填哪个路径、MySQL 8.0 的认证插件为什么连不上……每个坑都能卡你半小时。这套环境搭完并稳定运行了几个项目之后,我把完整过程整理成这篇实操记录。整个思路其实很清晰:在 Windows Server 2016 上启用 IIS 10.0,装好 PHP 并以 FastCGI 方式跑起来,再部署 MySQL 完成数据存储,最后把站点发布出去。适合刚接触 Windows 服务器运维、需要快速搭建 PHP 运行环境的人参考。
我默认读者已经有一台能正常联网的 Windows Server 2016 服务器,手上的 PHP 程序是开源的,部署方式以复制文件为主。如果是为了学习或测试,随便一台虚拟机也能撑住,配置不用太高,2 核 4G 内存跑这套环境完全够用。下面每个步骤我都会说明为什么要这么做,以及我踩过的那些坑。
1. 环境准备与整体思路
1.1 为什么选 Windows Server 2016 + IIS + FastCGI
很多运维对 PHP 的固有印象是“跑在 Linux + Apache/Nginx 上”,但其实 Windows 平台跑 PHP 非常成熟,尤其是 IIS + FastCGI 的组合,在微软相关生态里已经打磨了十几年。Windows Server 2016 自带的 IIS 10.0 原生支持 FastCGI 协议,不需要额外装第三方扩展,IIS 管理器里直接就能配置 PHP 处理映射,操作起来比在 Linux 上改 Apache 配置更直观,适合习惯了图形界面操作的运维。
FastCGI 相比传统的 CGI 模式最大的优势是进程复用:CGI 每次请求都要重新启动 PHP 进程,高并发下性能很差;FastCGI 则是常驻进程,IIS 维护一个 PHP-CGI 进程池,请求来了直接分发,性能提升非常明显。IIS 10.0 对 FastCGI 的支持很成熟,进程回收、实例数管理、环境变量配置这些都能在 IIS 管理器里完成,这套组合在 Windows 生态里算是标准答案。
选择 Windows Server 2016 还有个实际原因:很多企业现有的基础设施(域控、文件服务器、SQL Server)都是 Windows 体系,把这套 PHP 环境直接跑在同体系服务器上,有利于统一管理和权限控制,尤其对不熟悉命令行的运维来说,上手成本低很多。
提示:如果服务器需要远程桌面管理,Windows Server 2016 默认允许两种管理员连接方式,记得规划好 RDP 端口和防火墙策略。如果你之前部署过 RDS 会话主机,并且系统有补丁重启后出现“其他会话主机提示 60 分钟后断连”之类的情况,需要先处理这些系统层面的问题,再开始装环境,别带着隐患往下走。
1.2 版本选型与下载准备
版本匹配是这套环境最容易翻车的环节,我单独列一节重点讲。Windows 上的 PHP 版本选择,核心看两点:PHP 官方发布的 Windows 构建是哪些,以及你的程序兼容哪个版本。
PHP 官方为 Windows 平台提供两种构建:Thread Safe(线程安全版,简称 TS)和Non Thread Safe(非线程安全版,简称 NTS)。IIS 以 FastCGI 方式运行 PHP 时,必须选择Non Thread Safe版本。原因在于:FastCGI 模式下 PHP 以单进程多请求模型运行,不需要线程安全机制,NTS 版本在这种场景下性能更好、稳定性更强。而 Apache 的 module 模式或 IIS 的 ISAPI 模式(这种方式已经淘汰)才需要 TS 版本。网上很多教程直接给了 TS 版下载链接,装完 IIS 跑起来频繁崩溃或 “FastCGI 进程意外退出”,多半就是这个问题。
PHP 大版本方面,如果跑的是老程序,建议 PHP 7.4(这是经典稳定版本,兼容性极广);如果是新项目,PHP 8.0、8.1 甚至 8.2 都可以。我当时用的是 PHP 7.4.30,配合老项目兼容性很好,多年积累的 PHP 程序基本不会有兼容问题。另外,不管装哪个版本,都必须确认系统安装了对应的Visual C++ Redistributable运行库,PHP 在 Windows 上的可执行文件依赖这些 VC 库。PHP 7.4 对应 VC15(Visual Studio 2015-2022),PHP 8.0 以上对应 VC17,去微软官网下载最新版 VC 运行库合集安装即可,一次性解决。
MySQL 的版本选择也有讲究。MySQL 5.7 和 MySQL 8.0 是当前最常用的两个大版本。如果 PHP 项目比较老,优先选 5.7,因为 MySQL 8.0 默认认证插件是caching_sha2_password,老版本的 PHP MySQL 扩展(特别是 mysqlnd 比较旧的版本)连接 8.0 会认证失败,虽然可以改成mysql_native_password,但多一道操作。新项目直接用 8.0 没毛病,功能更强,默认字符集 utf8mb4 也更友好。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| 操作系统 | Windows Server 2016 | 自带 IIS 10.0,支持 FastCGI |
| PHP | 7.4.x NTS x64 | 稳定、兼容性好;新项目可选 8.0+ |
| MySQL | 5.7.x 或 8.0.x | 老项目用 5.7,新项目可用 8.0 |
| VC 运行库 | VC14/VC15/VC17 | 按 PHP 版本提示安装对应版本 |
1.3 系统初始化与目录规划
动手前先做基础配置:把服务器的计算机名改掉(别是乱七八糟的临时名字),固定 IP 地址,关闭 IE 增强安全配置(如果你图省事,当然留着也不影响命令行操作,但浏览器访问测试时会被安全弹窗烦死),然后做完 Windows Update 重启。
目录规划是很多新手容易忽略的细节。我习惯把环境目录和数据目录分开:
C:\PHP —— PHP 程序目录 C:\wwwroot —— 网站根目录(按下级域名创建子目录) C:\mysql_data —— MySQL 数据目录(或使用 MySQL 默认) C:\backup —— 数据库备份目录注意,不要把 PHP 解压到C:\Program Files下,路径里有空格会出现不可预知的路径问题,虽然通常能处理,但遇到奇葩情况排查起来很浪费时间。直接放盘符根目录或C:\Tools这种无空格路径是最省心的。
实操心得:我亲自遇到过因为路径里有空格导致 PHP 扩展加载失败的情况,换成
C:\PHP后问题立刻消失。所以这里不要我解释为什么了,避开风险路径就是给自己省事。
2. 启用 IIS 10.0 并配置 FastCGI 支持
2.1 添加 IIS 角色和 CGI 功能
Windows Server 2016 的 IIS 默认没开启,需要手动从“服务器管理器”添加。打开“服务器管理器”,点击“添加角色和功能”,进入安装向导后一路下一步,到“服务器角色”页面勾选Web 服务器(IIS),然后会看到它连带勾选了一堆默认子项。
关键是继续展开“应用程序开发”这一节,确保勾选了CGI。FastCGI 是 CGI 协议的增强实现,IIS 的 CGI 功能组件里包含了 FastCGI 支持,如果漏掉这一项,后面添加 PHP 处理程序映射时会发现根本没有 FastCgiModule 可选。
其他组件按需勾选,常用的包括:
- 常见 HTTP 功能:默认文档、静态内容、HTTP 错误(默认已勾选)
- 运行状况和诊断:HTTP 日志、请求监视
- 安全性:IP 和域限制(有需要再开)
安装完成后,打开浏览器访问http://localhost,能看到 IIS 默认欢迎页就说明 IIS 装好了。
注意:IIS 装好后默认站点绑定 80 端口,如果你的业务还需要 Nginx 或其他 Web 服务器占用 80,提前规划好端口分配,别装完再打架。热词里有人搜“windows server 2016 部署 nginx”,那是在这套 IIS 之外另起一套 Web 服务,两者可以共存但要注意端口、日志、进程身份等资源的隔离。
2.2 Web 平台安装程序(Web PI)还是手动配置
微软官方有一个Web Platform Installer(Web PI)工具,理论上可以一键安装 PHP、MySQL 等组件。但我在实际部署中一般不用它来装核心环境,原因有几个:Web PI 安装的 PHP 版本通常不是最新,而且组件版本选择有限,很难精确匹配项目需求;另外 Web PI 将 PHP 和 MySQL 按默认路径安装,后期调整目录很不灵活。
更推荐手动下载安装包并配置,虽然前期多花十几分钟,但每一步都在自己掌控下,之后排查问题思路清晰。手动配置这套环境,不上手一次你永远不会理解 IIS 的 FastCGI 映射是怎么工作的。
2.3 为 PHP 站点创建应用池和网站
IIS 中的“应用程序池”本质上是一个进程隔离单位,每个池可以有独立的 .NET CLR 版本和进程设置。PHP 站点通常不需要 .NET 运行环境,可以创建一个应用池并设置“无托管代码”,避免不必要的 CLR 加载开销。
具体操作:
- 打开 IIS 管理器,在左侧树形菜单中找到“应用程序池”,右键“添加应用程序池”。
- 名称随意(如
php_pool),“.NET CLR 版本”选择无托管代码,“托管管道模式”选集成。 - 在“网站”节点右键“添加网站”,设置网站名称(如
php_site),选择刚创建的应用池,物理路径指向C:\wwwroot下建好的站点目录(例如C:\wwwroot\demo),主机名可以不填(用 IP+端口访问)。 - 端口默认 80,如果 80 被占(比如 IIS 默认站点占着),要么停掉默认站点,要么给新站点换一个端口(如 8080)。
这里强调一下:IIS 默认创建的“Default Web Site”不建议直接拿来跑业务站点,专门为每个业务建一个独立站点 + 独立应用池,才能避免多个站点相互干扰,这也是生产环境的通行做法。
3. PHP 环境部署与 FastCGI 映射
3.1 PHP 安装包解压与基础配置
从 Windows.php.net 下载 PHP 7.4 NTS x64 的 zip 包,解压到C:\PHP。目录结构大概是这样:
C:\PHP\php.exe C:\PHP\php-cgi.exe C:\PHP\php.ini-development C:\PHP\php.ini-production C:\PHP\ext\ —— PHP 扩展目录 C:\PHP\extras\ —— 帮助文档等PHP 官方没有提供现成的 php.ini,需要手动从模板复制一份出来。把php.ini-development复制一份重命名为php.ini,然后修改以下几个核心配置:
extension_dir = "C:\PHP\ext"这是扩展目录路径,不设置的话,开启任何扩展都会失败。注意路径分隔符用反斜杠,结尾不需要再加斜杠。
date.timezone = Asia/Shanghai不设置时区,PHP 会警告 “date.timezone is not set”,并且日期函数会差 8 小时。
勾选需要的扩展,把对应行前的分号去掉。基础运行的必备扩展:
extension=curl extension=fileinfo extension=mbstring extension=mysqli extension=openssl extension=pdo_mysql这几个扩展的用途分别是:mysqli和pdo_mysql是 PHP 连接 MySQL 的两种驱动方式(新代码推荐 PDO,老代码用 mysqli),mbstring处理多字节字符串,curl做 HTTP 请求,openssl支持 HTTPS 相关操作,fileinfo用于文件上传的类型检测(很多框架依赖它)。如果你的程序用到 GD 库生成图片,还要开extension=gd,热词里有人搜“php图片生产”,指的就是这类用 PHP 动态输出图片或验证码的场景。
实操心得:我在配置的时候习惯把
extension=php_openssl.dll中的php_前缀去掉——因为新版 PHP 7.4 扩展文件名直接是php_openssl.dll还是openssl.dll,不同版本有差异。这里有个坑:如果扩展文件名写错,PHP 启动时会报错,且错误可能不会直接显示在页面上,而是写到 Windows 事件日志里。排查时可以先用命令行执行C:\PHP\php.exe -v,如果扩展配置有误,命令行会直接报出具体错误。
上传大小限制相关配置,根据业务需要调整:
upload_max_filesize = 20M post_max_size = 20M max_execution_time = 120 max_input_time = 120 memory_limit = 128M注意:很多 PHP 程序上传文件提示“文件超过限制”,却不一定是 PHP 层配置的问题,IIS 本身还有一层请求筛选(Request Filtering)默认限制 30MB,超过这个值还没到 PHP 就被 IIS 拦截了,这个坑后面会展开说。
3.2 在 IIS 中添加 FastCGI 映射和处理程序
这一步是整个环境搭建的临门一脚。打开 IIS 管理器,找到左侧的服务器根节点(不是具体某个网站),双击处理程序映射,然后点击右侧的“添加模块映射”,填写:
- 请求路径:
*.php - 模块:选择
FastCgiModule - 可执行文件:
C:\PHP\php-cgi.exe - 名称:
PHP_via_FastCGI
保存时如果弹出“是否注册 FastCGI 应用程序?”的提示,直接点是。之后 IIS 会自动创建一个 FastCGI 应用程序,指向php-cgi.exe。
还有一种更精细的做法:在服务器根节点的FastCGI 设置中手动添加应用程序,可配置环境变量PHPRC指向 PHP 目录,用来告诉 PHP 到哪里找php.ini:
Name: C:\PHP\php-cgi.exe EnvironmentVariables: PHPRC = C:\PHP用PHPRC环境变量的好处是,多个 PHP 版本共存时每个版本可以指定不同配置文件,互不干扰。如果你只有一个 PHP 版本,用第一种简单的添加方法就够。
然后别忘了,如果你刚才创建的 FastCGI 处理程序只加在了服务器级别,它会对所有站点生效;也可以在特定站点上重复“处理程序映射”操作,把映射限定到单个站点,灵活度更高。生产环境里我一般把公用映射放服务器级别,特殊站点单独覆盖。
3.3 验证 PHP 是否正常解析与常见 500 错误
在网站根目录(比如C:\wwwroot\demo)新建一个test.php文件:
<?php phpinfo();然后访问http://服务器IP/test.php,浏览器能看到 PHP 版本信息大页面,就说明 PHP 已经通过 FastCGI 跑起来了。phpinfo 页面上可以确认几项关键信息:
PHP Version与架构(x64 或 x86),确认装的是 NTS 版本Server API一栏显示CGI/FastCGI,说明 IIS 是通过 FastCGI 调用的 PHPLoaded Configuration File显示C:\PHP\php.ini,确认 PHPRC 环境变量生效
如果访问时出现 500 错误,最常见原因有三类:
php-cgi.exe缺少 VC 运行库,事件查看器里能看到类似无法加载 php-cgi.exe的错误,装对应 VC 库即可。php.ini中写了不存在的扩展名,PHP 启动时加载失败。命令行运行php -v是最直接的排查方式,错误会直接打印。- 权限问题:IIS 进程身份(默认
IUSR或应用池身份)对C:\PHP目录没有读取和执行权限。打开C:\PHP的安全属性,给IIS_IUSRS组添加“读取和执行”权限。
细节很重要,权限不放过的话,后面每个请求都会 500,而且错误日志里你根本看不出来是谁拒绝的,只能靠排查。
4. MySQL 安装与配置
4.1 下载与初始化 MySQL
MySQL 官方提供两种安装方式:MSI 安装包和 ZIP 压缩包。我推荐 ZIP 方式,原因和上面说 PHP 的理由一致:可控、好迁移、清楚装了什么。下载时注意选Windows (x86, 64-bit), ZIP Archive版本,解压到C:\mysql(依然避开空格路径)。
解压后,MySQL 不会自动初始化数据目录,需要手动执行。以管理员身份打开 CMD,进入 MySQL 的 bin 目录:
cd C:\mysql\bin mysqld --initialize-insecure --basedir=C:\mysql --datadir=C:\mysql\data--initialize-insecure的含义是初始化数据目录,并且 root 用户初始密码为空。如果不带insecure参数,MySQL 会生成一个随机临时密码,打印在命令行输出里但稍纵即逝,很容易错过。对新手来说,用insecure初始化后自己立刻设置密码,是更可控的做法。
初始化完成后,注册成 Windows 服务,方便开机自启:
mysqld --install MySQL80 --display-name "MySQL80"然后启动服务:
net start MySQL80如果服务无法启动,去查看 MySQL 目录下的data文件夹里的错误日志(.err文件),里面会写明具体原因。最常见的是datadir路径不存在或没有写权限,确认之前初始化步骤没有报错即可。
实操心得:很多人初始化 MySQL 时喜欢直接用
mysqld --initialize(不带 insecure),然后在命令行输出的末尾找到那一长串随机密码。但如果你用远程桌面操作,窗口缓冲不够或不小心关掉窗口,密码就丢了。我用--initialize-insecure初始化后,登录进去自己改密码,整个过程完全可控。密码策略建议设置成复杂密码,但也要兼顾自己记得住,最好放到密码管理工具里。
4.2 登录并修改 root 密码
用空密码登录 MySQL:
mysql -u root -p提示输入密码时直接回车。然后执行:
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的安全密码'; FLUSH PRIVILEGES;注意:MySQL 8.0 默认的密码认证插件是
caching_sha2_password。如果你用 MySQL 5.7 或更老的工具(如某些老版本的 Navicat、PHP 的 mysqlnd 扩展)连接,可能会报Authentication plugin 'caching_sha2_password' cannot be loaded。解决办法是创建一个使用mysql_native_password认证的账号,或者把现有账号的认证方式改为兼容模式:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的安全密码';这也是网上很多人搜“mysql 安装配置教程”时最容易卡住的地方。不怕麻烦就提前了解清楚,免得后面 PHP 连不上数据库还在那怀疑端口和防火墙。
4.3 创建业务数据库和专用账号
生产环境强烈反对用 root 账号连接应用,这是基本的安全红线。为每个项目创建独立数据库和最小权限账号:
CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'demo_user'@'localhost' IDENTIFIED BY '密码'; CREATE USER 'demo_user'@'127.0.0.1' IDENTIFIED BY '密码'; GRANT ALL PRIVILEGES ON demo_db.* TO 'demo_user'@'localhost'; GRANT ALL PRIVILEGES ON demo_db.* TO 'demo_user'@'127.0.0.1'; FLUSH PRIVILEGES;字符集选择utf8mb4而不是utf8,这点特别重要。utf8 在 MySQL 里最多只能存 3 个字节的字符,很多 emoji 表情和生僻字存不进去,程序一写就报错。utf8mb4 是 utf8 的超集,兼容性最好。
localhost和127.0.0.1都要建账号,是因为有些驱动用 TCP/IP 连接时会解析到 127.0.0.1,有些直接用 localhost 走系统管道,两个都建才万无一失。
4.4 使用 Navicat / MySQL Workbench 连接管理
日常管理数据库建议装个图形化客户端。MySQL 官方免费的MySQL Workbench足够用,也可以在热词里看到的Navicat for MySQL(收费但有试用版)。连接时主机填服务器 IP(本机就填 127.0.0.1),端口 3306,用户名和密码用刚才创建的账号。
连接不上的常见原因:
- MySQL 没监听 3306 或防火墙拦截。确认服务运行中,且 Windows 防火墙出站入站规则允许 3306 端口。用远程连接测试时尤其注意检查防火墙。
- root 默认只允许 localhost 连接,从别的机器连不上。所以前面建议创建业务账号时同时建了 127.0.0.1 的账号;如果需要远程管理,还得建
'demo_user'@'%'(按需,安全起见生产环境不建议开放)。 - MySQL 8.0 的认证插件不兼容旧客户端,按 4.2 的 SQL 改成
mysql_native_password。
5. 部署 PHP 站点并连接 MySQL
5.1 复制源码与配置 IIS 目录权限
现在核心环境都齐了,可以部署真实的 PHP 项目。将要部署的程序源码复制到站点目录,比如C:\wwwroot\demo。这一步的关键在于 IIS 进程身份对该目录的权限。
IIS 默认的应用池身份是ApplicationPoolIdentity,在“计算机管理 → 本地用户和组”里看不到这个账号,它是动态生成的虚拟账号。访问网站文件时,IIS 会以应用池身份去读取物理路径,因此需要给站点目录授权:
icacls "C:\wwwroot\demo" /grant "IIS_IUSRS:(OI)(CI)RX" /T如果站点需要写入文件(比如上传图片、生成缓存、写日志),需要给IIS_IUSRS添加修改(M)权限:
icacls "C:\wwwroot\demo" /grant "IIS_IUSRS:(OI)(CI)M" /T实操心得:权限这块是很多新手最容易懵的地方。你要区分两个东西:一个是IUSR(匿名用户),一个是IIS_IUSRS(IIS 进程账号组)。大多数情况下只给 IIS_IUSRS 授权就够了,但如果你把网站“身份验证 → 匿名身份验证”改成了“IUSR”,那 IUSR 也需要对应权限。我个人的习惯是统一用应用池身份跑站点、给 IIS_IUSRS 授权,这样改动最小,排查也简单。
5.2 测试 PHP 连接 MySQL
在站点根目录创建db_test.php:
<?php $host = '127.0.0.1'; $user = 'demo_user'; $pass = '你的密码'; $db = 'demo_db'; $conn = new mysqli($host, $user, $pass, $db); if ($conn->connect_error) { die('连接失败: ' . $conn->connect_error); } $conn->set_charset('utf8mb4'); echo 'MySQL 连接成功'; $conn->close();访问这个文件,页面显示“MySQL 连接成功”就说明 PHP 和 MySQL 完全打通了。这一步是检验前面所有工作的终极测试。如果这一步挂了,从头排查,不要怀疑人生:
| 现象 | 可能原因 |
|---|---|
| 连接失败 Access denied | 账号密码错误、MySQL 8.0 认证插件不兼容、账号 host 不匹配 |
| 连接失败 Connection refused | MySQL 服务未启动、端口被防火墙拦截、PHP 连接 host 配错 |
| 服务器 500 错误 | PHP 的 mysqli 扩展没开启、PHP 配置错误 |
5.3 将站点发布到外网与域名绑定
内网测试通过后,如果要对外开放,需要做好几件事:
- 将域名解析到服务器公网 IP。
- IIS 站点绑定域名:右键网站 → 绑定 → 添加绑定,类型 HTTP,主机名填你的域名,如果配了 HTTPS(强烈推荐),再添加一条 443 绑定并选择证书。
- 云服务器的话,在控制台的安全组里放行入方向 80/443 端口;本地物理服务器则是在 Windows 防火墙里建入站规则放行对应端口,或直接把“万维网服务(HTTP)”规则启用。
这里提一点实际的:Windows 上申请和管理 HTTPS 证书,最省事的是用 Let‘s Encrypt 的 Windows 客户端(比如 Certbot 或 win-acme),可以自动续期。如果你以前用 Nginx 部署过证书,明白原理就行,IIS 无非是把证书导入到“服务器证书”模块,再在站点绑定里选上,操作并不复杂。
5.4 PHP 运行模式检查
站点上线后,再用 phpinfo 小页面确认一下运行模式,或者写个探针文件查看:
<?php echo php_sapi_name();输出应该是cgi-fcgi或fpm-fcgi,只要不是apache2handler或isapi,就说明 FastCGI 模式正常。这个检查很初级,但很多人装完了没确认,等到后面调性能才发现模式不对。
6. 常见问题、安全加固与日常运维
6.1 高频问题排查实录
我整理了几个在实际部署中最常遇到的问题,以及对应的排查思路。
现象 1:访问 PHP 文件返 500.0 错误
500.0 是 IIS 层面的错误,通常在浏览器里只显示 “HTTP Error 500.0 - Internal Server Error”。查看 Windows 事件查看器(应用程序日志),搜索来源为FastCgi的错误记录。最常见的原因是:
- VC 运行库没装,事件日志里报找不到
MSVCP140.dll之类的错误。 - PHP 扩展加载失败,事件日志里提示加载某个
php_*.dll失败。解决方法是注释掉php.ini里对应的扩展行。 php.ini语法错误,检查是否有中文字符或非法配置项。
现象 2:PHP 页面显示“未找到 mysqli 扩展”
mysqli是 PHP 连接 MySQL 的扩展,必须保证两点:php.ini里extension=mysqli(7.4 中写法为extension=mysqli)被启用,且extension_dir路径正确。配置改完记得重启 IIS——在 IIS 管理器右侧点击“重新启动”或命令行运行iisreset,FastCGI 进程常驻,不重启配置不会生效。
现象 3:PHP 可以访问,但连不上 MySQL
先确认 MySQL 服务状态、监听端口、防火墙。如果用的 MySQL 8.0,认证插件不兼容也可能导致连接失败,按 4.2 节给的 SQL 把认证方式改掉。
现象 4:上传大文件总是失败
这种问题要检查多个层级:IIS 请求筛选限制(默认 30MB,在站点“请求筛选 → 编辑功能设置”里调大)、PHP 的upload_max_filesize和post_max_size、脚本超时时间max_execution_time。三层都要调整,缺一不可。
现象 5:PHP 站点能访问,但 CMS/框架提示缺少 PHP 扩展
比如有的程序需要php_fileinfo、php_zip、php_gd2,都是同一个原因:这些扩展默认没启用。去php.ini里去掉对应扩展前的分号,重启 IIS 即可。注意早期版本扩展名带php_前缀(比如php_gd2.dll),7.4 后统一为gd,写错了同样加载失败。
6.2 安全加固的重要提醒
环境能跑起来只是第一步,上线前安全加固必不可少。这里说几个我踩过坑后总结的要点。
关闭错误信息显示,开启错误日志:php.ini中设置display_errors = Off,日志开启log_errors = On,并指定error_log = C:\PHP\logs\php_errors.log。生产环境暴露 PHP 报错信息等于告诉攻击者你的路径、数据库状态甚至代码逻辑,这是在给攻击者递刀。
禁用危险函数:很多被攻击的 PHP 程序,问题出在eval、system、exec、shell_exec等函数被利用。在php.ini里添加:
disable_functions = system, exec, shell_exec, passthru, popen, proc_open业务确实不需要这些函数的话,禁掉能挡掉一大波恶意代码。注意:如果你要跑 Composer 或一些需要执行系统命令的框架,这些函数可能要临时放开,生产机自己权衡。
目录权限最小化:网站目录只给运行所需的最小权限。写入权限只给明确需要的子目录(如 upload、runtime),不要整个站点目录都给写权限。热词里有人搜“php 上传漏洞”,最常见的原因就是目录权限过大 + 未校验上传文件类型,攻击者上传了 PHP 木马后还能执行。Upload 目录即使需要写,也应该通过 IIS 请求筛选禁用该目录的 PHP 执行权限。
MySQL 安全:
- 生产环境不要用 root 连接应用,专库专号。
- root 密码务必强密码,长度至少 12 位,包含大小写、数字和符号。
- 不需要远程访问时,不要开放 3306 端口到公网。
6.3 数据库自动备份脚本
数据无价,备份是运维最基本的仪式感。我写了个简单的 Windows 批处理脚本,配合计划任务实现每天自动备份:
@echo off set BACKUP_DIR=C:\backup set DB_USER=root set DB_PASS=你的密码 set DB_NAME=demo_db for /f "tokens=1-3 delims=/ " %%a in ('date /t') do set TODAY=%%c%%a%%b "C:\mysql\bin\mysqldump" -u%DB_USER% -p%DB_PASS% %DB_NAME% > "%BACKUP_DIR%\%DB_NAME%_%TODAY%.sql" echo 备份完成: %BACKUP_DIR%\%DB_NAME%_%TODAY%.sql在“任务计划程序”中创建基本任务,触发器设置为每天凌晨(如 02:00),操作定位到这个 bat 文件。生产环境建议额外设置保留天数,定期清理旧备份,例如保留最近 7 天的 SQL 文件即可。这个脚本是我用了很久的基础款,你也可以在此基础上扩展成压缩备份、异地同步,核心思路是一样的。
6.4 性能调优建议
环境稳定运行后,如果遇到性能问题,可以从这几个方向入手:
调整 FastCGI 进程池实例数:IIS 管理器 → 服务器根节点 → FastCGI 设置 → 选中 PHP 应用程序 → 编辑,可以看到“实例数”设置。默认值是Max,理论上自动管理,但在高并发场景下实例数过少会导致进程排队,过多会吃满内存。2 核 4G 的机器建议设成 2-4,8G 内存可以试 8,具体依实际压测结果调整。
开启 PHP OPcache:php.ini中启用:
[opcache] zend_extension=opcache opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60OPcache 会将 PHP 编译后的字节码缓存在内存里,省去每次请求重新编译解析的开销。对于纯 PHP 站点来说,这是最简单且提升明显的优化手段。注意文件名是php_opcache.dll还是opcache.dll,不同 PHP 版本有差异,写错了会加载失败。
MySQL 缓存与连接池:MySQL 5.7 默认不会自动调出很大的缓存,如果你服务器的内存充足,可以在my.ini中调整innodb_buffer_pool_size(建议物理内存的 50%-70%)。8.0 版本默认自动管理得更好,但依然可以手动调。改完my.ini后重启 MySQL 服务生效。
6.5 用命令行快速管理环境的几个技巧
日常运维不需要每次都打开 IIS 管理器,记住几个命令能提高效率:
# 重启 IIS(PHP 配置改动后经常用) iisreset # 重启 MySQL 服务 net stop MySQL80 && net start MySQL80 # 快速测试 PHP 配置是否有误 C:\PHP\php.exe -v # 查看 PHP 已加载哪些扩展 C:\PHP\php.exe -m # 手动执行 SQL 脚本 C:\mysql\bin\mysql -u root -p -e "source C:\backup\backup.sql"iisreset是全量重启 IIS,如果有多个站点在跑,用它会同时中断所有站点。如果只想重启单一应用池,在 IIS 管理器里对应应用池上右键“回收”,或者用 PowerShell:
Restart-WebAppPool -Name "php_pool"实际运维中,这两个操作我会区分使用,避免因为改一个站点而影响其他站点。
最后再分享一个切身体会:这套环境装过一次之后,你就会发现 Windows 平台上跑 PHP 并没有想象中那么脆弱,IIS 的 FastCGI 稳定性其实相当好,配合图形化管理,日常维护成本远比命令行的 Linux 环境低。我在这套环境上跑过图书管理系统、企业门户、接口服务等不同类型项目,运行几个月不重启都不会出问题。如果你用的是老 PHP 项目,建议装好后先测一遍功能再正式切流量,避免项目代码里用到某些已废弃的旧函数导致运行异常。这套流程你走一遍之后,以后再装新环境基本半小时就能搞定,版本、权限、防火墙这些关键点心里有数,踩坑概率就低多了。