Veruse / fix-wp-permissions.sh

Last active 3 weeks ago

Like 0

bash <(curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions/raw/HEAD/fix-wp-permissions.sh)

Veruse revised this gist 3 weeks ago · 9e65b5e

1 file changed, 3 insertions, 3 deletions

fix-wp-permissions.sh
@@ -10,8 +10,8 @@
10 10 # wp-content/uploads writable by the web server group (775 dirs / 664 files) with --group-write
11 11 #
12 12 # Run from a URL (pass options after "bash -s --"):
13 - # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
14 - # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | sudo bash -s -- -p /var/www/example.com -o deploy -g www-data --group-write
13 + # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions/raw/HEAD/fix-wp-permissions.sh | bash -s -- --dry-run
14 + # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions/raw/HEAD/fix-wp-permissions.sh | sudo bash -s -- -p /var/www/example.com -o deploy -g www-data --group-write
15 15 #
16 16 # Or locally:
17 17 # ./fix-wp-permissions.sh [-p path] [-o owner] [-g group] [--group-write] [--strict] [--dry-run]
@@ -43,7 +43,7 @@ Usage: fix-wp-permissions.sh [options]
43 43 -h, --help show this help
44 44
45 45 From a URL:
46 - curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
46 + curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions/raw/HEAD/fix-wp-permissions.sh | bash -s -- --dry-run
47 47 USAGE
48 48 exit "${1:-0}"
49 49 }

Veruse revised this gist 3 weeks ago · 20a1a21

1 file changed, 155 insertions

fix-wp-permissions.sh (file created)
@@ -0,0 +1,155 @@
1 + #!/usr/bin/env bash
2 + #
3 + # fix-wp-permissions.sh — reset file and folder permissions on a WordPress install.
4 + #
5 + # Defaults (WordPress-recommended):
6 + # directories 755
7 + # files 644
8 + # wp-config.php 640 (use --strict for 600)
9 + # .htaccess 644
10 + # wp-content/uploads writable by the web server group (775 dirs / 664 files) with --group-write
11 + #
12 + # Run from a URL (pass options after "bash -s --"):
13 + # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
14 + # curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | sudo bash -s -- -p /var/www/example.com -o deploy -g www-data --group-write
15 + #
16 + # Or locally:
17 + # ./fix-wp-permissions.sh [-p path] [-o owner] [-g group] [--group-write] [--strict] [--dry-run]
18 + #
19 + # With no -p, the current directory is used.
20 +
21 + # Everything runs inside main(), called on the last line, so a partially
22 + # downloaded script never executes.
23 + main() {
24 + set -euo pipefail
25 +
26 + WP_PATH="$PWD"
27 + OWNER=""
28 + GROUP=""
29 + GROUP_WRITE=false
30 + STRICT=false
31 + DRY_RUN=false
32 +
33 + usage() {
34 + cat <<'USAGE'
35 + Usage: fix-wp-permissions.sh [options]
36 +
37 + -p, --path DIR WordPress root (default: current directory)
38 + -o, --owner USER chown files to USER (needs root)
39 + -g, --group GROUP chown files to GROUP, e.g. www-data (needs root)
40 + --group-write make wp-content/uploads writable by the group (775/664)
41 + --strict wp-config.php 600 instead of 640
42 + -n, --dry-run show what would change, change nothing
43 + -h, --help show this help
44 +
45 + From a URL:
46 + curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
47 + USAGE
48 + exit "${1:-0}"
49 + }
50 +
51 + while [[ $# -gt 0 ]]; do
52 + case "$1" in
53 + -p|--path) WP_PATH="${2:-}"; shift 2 ;;
54 + -o|--owner) OWNER="${2:-}"; shift 2 ;;
55 + -g|--group) GROUP="${2:-}"; shift 2 ;;
56 + --group-write) GROUP_WRITE=true; shift ;;
57 + --strict) STRICT=true; shift ;;
58 + -n|--dry-run) DRY_RUN=true; shift ;;
59 + -h|--help) usage 0 ;;
60 + *) echo "Unknown option: $1" >&2; usage 1 ;;
61 + esac
62 + done
63 +
64 + # --- Sanity checks ---------------------------------------------------------
65 +
66 + [[ -z "$WP_PATH" ]] && { echo "Error: --path is empty." >&2; usage 1; }
67 + WP_PATH="$(cd "$WP_PATH" 2>/dev/null && pwd)" || { echo "Error: path not found." >&2; exit 1; }
68 +
69 + if [[ "$WP_PATH" == "/" || "$WP_PATH" == "$HOME" && ! -d "$WP_PATH/wp-includes" ]]; then
70 + echo "Error: refusing to run on '$WP_PATH'." >&2
71 + exit 1
72 + fi
73 +
74 + if [[ ! -d "$WP_PATH/wp-includes" || ! -d "$WP_PATH/wp-admin" ]]; then
75 + echo "Error: '$WP_PATH' doesn't look like a WordPress root (no wp-includes/ or wp-admin/)." >&2
76 + exit 1
77 + fi
78 +
79 + # wp-config.php can live in the WP root or one level above it.
80 + WP_CONFIG=""
81 + if [[ -f "$WP_PATH/wp-config.php" ]]; then WP_CONFIG="$WP_PATH/wp-config.php"
82 + elif [[ -f "$WP_PATH/../wp-config.php" ]]; then WP_CONFIG="$(cd "$WP_PATH/.." && pwd)/wp-config.php"
83 + fi
84 +
85 + if [[ -n "$OWNER" || -n "$GROUP" ]] && [[ $EUID -ne 0 ]]; then
86 + echo "Error: changing ownership needs root. Re-run with sudo, or drop -o/-g." >&2
87 + exit 1
88 + fi
89 + [[ -n "$OWNER" ]] && ! id -u "$OWNER" &>/dev/null && { echo "Error: user '$OWNER' doesn't exist." >&2; exit 1; }
90 + [[ -n "$GROUP" ]] && ! getent group "$GROUP" &>/dev/null && { echo "Error: group '$GROUP' doesn't exist." >&2; exit 1; }
91 +
92 + run() {
93 + if $DRY_RUN; then echo "[dry-run] $*"; else "$@"; fi
94 + }
95 +
96 + echo "WordPress root : $WP_PATH"
97 + echo "wp-config.php : ${WP_CONFIG:-not found}"
98 + echo "Owner:group : ${OWNER:-(unchanged)}:${GROUP:-(unchanged)}"
99 + echo "Uploads writable by group: $GROUP_WRITE"
100 + $DRY_RUN && echo "DRY RUN — nothing will be changed."
101 + echo
102 +
103 + # --- Ownership -------------------------------------------------------------
104 +
105 + if [[ -n "$OWNER" || -n "$GROUP" ]]; then
106 + echo "Setting ownership..."
107 + run chown -R "${OWNER}${GROUP:+:$GROUP}" "$WP_PATH"
108 + [[ -n "$WP_CONFIG" && "$WP_CONFIG" != "$WP_PATH/wp-config.php" ]] && \
109 + run chown "${OWNER}${GROUP:+:$GROUP}" "$WP_CONFIG"
110 + fi
111 +
112 + # --- Base permissions ------------------------------------------------------
113 + # -P: don't follow symlinks; chmod on symlinks is skipped by -type d / -type f.
114 +
115 + echo "Setting directories to 755..."
116 + run find -P "$WP_PATH" -type d -exec chmod 755 {} +
117 +
118 + echo "Setting files to 644..."
119 + run find -P "$WP_PATH" -type f -exec chmod 644 {} +
120 +
121 + # --- Uploads ---------------------------------------------------------------
122 +
123 + UPLOADS="$WP_PATH/wp-content/uploads"
124 + if $GROUP_WRITE && [[ -d "$UPLOADS" ]]; then
125 + echo "Making uploads group-writable (775/664)..."
126 + run find -P "$UPLOADS" -type d -exec chmod 775 {} +
127 + run find -P "$UPLOADS" -type f -exec chmod 664 {} +
128 + # setgid so new uploads inherit the group
129 + run find -P "$UPLOADS" -type d -exec chmod g+s {} +
130 + fi
131 +
132 + # --- Sensitive files -------------------------------------------------------
133 +
134 + if [[ -n "$WP_CONFIG" ]]; then
135 + if $STRICT; then
136 + echo "Locking wp-config.php to 600..."
137 + run chmod 600 "$WP_CONFIG"
138 + else
139 + echo "Locking wp-config.php to 640..."
140 + run chmod 640 "$WP_CONFIG"
141 + fi
142 + fi
143 +
144 + [[ -f "$WP_PATH/.htaccess" ]] && run chmod 644 "$WP_PATH/.htaccess"
145 +
146 + # Any scripts/binaries that genuinely need +x (e.g. in vendor/bin) can be
147 + # re-added here, e.g.:
148 + # run find "$WP_PATH/wp-content" -path '*/vendor/bin/*' -type f -exec chmod 755 {} +
149 +
150 + echo
151 + echo "Done."
152 + $DRY_RUN || echo "Tip: load the site and test uploads, plugin updates and the media library."
153 + }
154 +
155 + main "$@"