BuddyX

13 min read · 2,572 words

Should I Upload Entire WordPress Site At Github

Should I Upload Entire WordPress Site At Github

The short answer is no, not the whole thing, not as one undivided folder. The more useful answer is that “should I use GitHub for my WordPress site” and “should I upload literally every file WordPress generates” are two different questions, and conflating them is exactly what leads people into trouble, either committing database credentials into a public history that never fully goes away, or bloating a repository with gigabytes of media that Git was never designed to handle well.

This isn’t a purely theoretical risk either. Leaked wp-config.php files, forgotten in public repositories, are a recurring, well-documented category of real-world credential exposure, security researchers and automated scanning bots specifically look for exactly this pattern across public GitHub, because it’s common enough to be worth scanning for at scale. Getting the boundary right from the start costs almost nothing extra; fixing it after the fact, once secrets are already sitting in history, is genuinely painful.

What Git Is Actually Good At

Git tracks changes to text-based files efficiently, diffing line by line, storing history in a way that makes reverting, branching, and merging genuinely fast even with years of accumulated commits. It is not designed around large binary files, a JPEG or an MP4 doesn’t diff meaningfully, Git just stores a new full copy every time the file changes even slightly, which means a media-heavy WordPress site tracked wholesale in Git balloons in size fast and gets slower to clone, push, and pull the longer it runs. That single fact should drive most of the decisions below: code goes in Git, binary media generally doesn’t.

What Actually Belongs in the Repository

Your theme files (if you’re building a custom theme rather than using an off-the-shelf one you don’t modify), any custom plugins your team writes, and configuration files that don’t contain secrets, these are exactly what Git was built for. Version history on a custom theme’s functions.php, the ability to branch off and test a feature without touching the live site’s files, and a clean audit trail of who changed what and when, all of that is real, tangible value a WordPress developer gets from Git that has nothing to do with whether the whole site lives there.

What Should Never Go In, and the .gitignore to Prove It

wp-config.php contains your database credentials, secret keys, and salts in plain text. Committing it, even once, even if you delete it in a later commit, means those credentials exist permanently in the repository’s history unless you go through the genuinely painful process of rewriting history with something like git filter-repo, and even then, if the repo was ever public or forked, you have to assume the leaked credentials are compromised and rotate them regardless. A functional .gitignore for a WordPress project looks roughly like this:

wp-config.php
wp-content/uploads/
wp-content/cache/
wp-content/upgrade/
wp-content/backup*/
*.sql
*.log
.env
node_modules/
vendor/

Uploads are excluded because they’re binary media, not code, and belong in a proper backup or asset pipeline instead. Cache and upgrade directories are transient, regenerated automatically, and add nothing but noise to the diff history. node_modules and vendor are excluded because they’re dependency trees that should be installed from a manifest (package.json, composer.json) rather than committed wholesale, committing them means every dependency update shows up as a massive, unreadable diff instead of a clean one-line version bump in the manifest file.

Managing Dependencies Properly Instead of Committing Them

If your custom theme or plugin pulls in third-party libraries, Composer (PHP) and npm (JavaScript build tooling) are the right tools for this, not manually zipping vendor folders into the repo. A composer.json declaring your PHP dependencies and their version constraints, committed to the repo, lets anyone (a new developer, a CI pipeline, your production deployment script) run composer install and get an identical, reproducible dependency tree without ever needing those files tracked in Git directly. The same logic applies to any JavaScript build step, commit package.json and package-lock.json, not the resulting node_modules folder, and run the install step as part of your deployment or CI process instead.

Handling the Database Separately

WordPress content, posts, pages, settings, users, lives in the database, not in files, and a database dump doesn’t belong in Git either, for the same reason media doesn’t: it’s not code, it changes constantly through normal site use rather than through deliberate developer commits, and every dump would sit in history as another full-size blob. For moving content between environments (local to staging to production), a dedicated tool built for exactly this handles it far better than manual SQL exports: WP Migrate DB (free and pro tiers) or WP-CLI’s own wp db export / wp search-replace combination for handling the URL and path differences between environments during a migration. Keep database handling and code version control as two separate, purpose-built processes rather than trying to force one tool to do both jobs.

A Realistic Repository Structure

Rather than the entire wp-content and WordPress core directory tree, a repository scoped to just what you’re actually developing looks more like:

my-project/
  wp-content/
    themes/
      my-custom-theme/
    plugins/
      my-custom-plugin/
  composer.json
  package.json
  .gitignore
  README.md

WordPress core itself and any third-party plugins installed from WordPress.org or a commercial vendor generally don’t need to be tracked either, they’re reproducible from a version number and a download, not something your team is actively editing. Some teams do choose to track core and third-party plugins anyway for a fully self-contained deployment history, that’s a legitimate choice for certain hosting setups, but it’s a deliberate tradeoff (repository size and noisy diffs on every WordPress core update) rather than a default you should reach for without thinking about it.

Environment-Specific Configuration Without Committing Secrets

Since wp-config.php itself shouldn’t be committed, the common pattern is to commit a wp-config-sample.php or use environment variables loaded through a library like vlucas/phpdotenv, reading database credentials, API keys, and debug flags from a .env file that itself is also excluded from Git and instead set up manually (or via your host’s environment variable settings) on each environment separately. This means your actual wp-config.php can safely reference getenv('DB_NAME') rather than a hardcoded value, and the same codebase works correctly across local, staging, and production just by having different environment variables set on each, without any of those values ever touching version control.

Private vs. Public Repositories

If a client’s custom theme or plugin code is proprietary, which it almost always should be treated as by default, use a private repository, not a public one. GitHub’s free tier includes unlimited private repositories for individual accounts and small teams, so cost generally isn’t a real obstacle here anymore the way it was on older GitHub pricing tiers. Public repositories make sense specifically when you’re intentionally open-sourcing a theme or plugin for the community, at which point the calculus flips entirely and you also want to double-check there’s genuinely nothing client-specific (a client’s domain hardcoded somewhere, test credentials, internal comments) sitting in the code before making it public.

Deployment: Getting Code From GitHub to a Live Site

Version control on its own doesn’t deploy anything, it’s a separate step, and skipping it means manually copying files over FTP after every commit, which defeats a lot of the point. GitHub Actions is the native option if you’re already on GitHub, a workflow file that runs on push to a specific branch and rsyncs or SFTPs changed files to your server, with secrets like SSH keys stored in GitHub’s encrypted repository secrets rather than in the workflow file itself. Managed deployment tools like DeployHQ or Buddy offer a more visual setup for teams who’d rather not hand-write YAML pipeline configs. Some hosts, WP Engine and Kinsta among them, and some tools like SpinupWP, offer native Git-based deployment as a built-in feature, connect a repo, choose a branch, push, done, which removes the need to build a custom Actions workflow at all if your hosting already supports it.

Submodules and mu-plugins Worth a Special Note

If your team maintains several WordPress projects that all share a common must-use plugin (site-wide functionality that shouldn’t be deactivatable, dropped into wp-content/mu-plugins), Git submodules let you keep that shared code in its own repository and reference it from multiple project repos without copy-pasting it into each one separately. Submodules have a reputation for being finicky, mostly because the workflow around updating a submodule’s pinned commit and remembering to run git submodule update --init --recursive after cloning trips people up the first several times, but for genuinely shared, actively maintained code across multiple projects, it beats maintaining diverging copies of the same file in five different repositories that quietly drift out of sync with each other over time.

Staging and Production as Separate Branches or Environments

A reasonably mature setup uses a main or production branch that maps to the live site and a staging or develop branch that maps to a staging environment, merging staging into production only after changes have actually been tested somewhere that isn’t the live site. This is table stakes in general software development but genuinely underused on WordPress projects specifically, where “edit the live theme file directly” is still a common (and risky) habit even among developers who’d never dream of doing the same on a non-WordPress codebase. Setting this up costs a bit of initial configuration and pays for itself the first time a change breaks something and you can revert a deploy in seconds instead of manually reconstructing what the file looked like before.

WP-CLI as the Glue Between All of This

WP-CLI is worth mentioning specifically because it’s the tool that makes a Git-based WordPress workflow practical rather than theoretical, scriptable plugin activation, database search-and-replace during environment moves, cache flushing, and user management all become commands you can call from a deployment script rather than manual admin-panel clicks that don’t fit into an automated pipeline. If you’re setting up Git-based deployment for the first time, learning WP-CLI’s basics alongside it pays off, since most of the “last mile” steps after files land on the server (running migrations, clearing a persistent object cache, updating a permalink structure) are things WP-CLI handles in one command rather than requiring you to log into wp-admin after every deploy.

If Secrets Already Got Committed

This happens more often than teams like to admit, someone commits wp-config.php early in a project before a proper .gitignore exists, and it sits in history for months before anyone notices. Deleting the file in a later commit does not remove it from history, anyone with clone access can still check out an earlier commit and read the old credentials directly. The correct remediation has two parts, and skipping either one leaves you exposed: first, actually rewrite history to strip the file out using git filter-repo (the currently recommended tool, having effectively replaced the older and slower git filter-branch) or the BFG Repo-Cleaner, and force-push the cleaned history, which everyone with a local clone then needs to re-clone or carefully rebase onto. Second, and this is the step people skip because the first step feels like it solved the problem: rotate every credential that was ever exposed, database password, secret keys and salts, any API keys, regardless of whether you believe anyone actually saw them. History rewriting removes the file from future clones, it doesn’t undo any exposure that already happened while it was there, and a repository that was ever public, even briefly, or forked by anyone, should be treated as fully compromised on that front.

Automated Secret Scanning as a Safety Net

Rather than relying purely on a correctly maintained .gitignore and human discipline, tools like gitleaks or GitHub’s own built-in secret scanning (enabled by default on public repos, available on private repos with GitHub Advanced Security) actively scan commits for patterns that look like API keys, database credentials, or private keys, and flag or block them before they land in history at all. Wiring this in as a pre-commit hook or a required GitHub Actions check catches the mistake at the moment it happens rather than months later during an audit, and it costs nothing but a few minutes of setup.

Git LFS for the Media You Genuinely Need Tracked

There are legitimate cases where some binary assets do need version tracking alongside code, brand assets a design team iterates on, or fixture images a theme’s demo content depends on. Git Large File Storage (Git LFS) exists specifically for this, it stores large binary files outside the main repository history and replaces them with lightweight pointer references inside Git itself, avoiding the repository bloat that comes from tracking binaries the normal way. It’s worth reaching for specifically when you have a real, ongoing need to version binary assets, not as a default solution for “I don’t want to think about where my uploads folder goes.”

Running Checks Before Code Ever Reaches Production

Once code lives in GitHub with a deployment pipeline attached, that pipeline is also the natural place to run automated checks before anything ships, PHP linting and PHPCS (WordPress Coding Standards) to catch style and common error patterns, PHPUnit tests if your custom plugin has any, and a basic syntax check (php -l) across changed files as a bare minimum. A GitHub Actions workflow that runs these checks on every pull request and blocks merging until they pass catches a meaningful share of bugs before they ever touch a live site, turning “we deployed a syntax error to production” from a scary emergency into something that simply can’t happen because the pipeline refused to let it through.

Licensing Considerations if You’re Sharing Code Publicly

If you do end up open-sourcing a theme or plugin built on WordPress, remember that WordPress itself is licensed GPL, and by extension, plugins and themes that hook into WordPress’s core functions are generally expected to be GPL-compatible too, this is a long-standing community norm backed by the license terms themselves, not just etiquette. Adding a clear LICENSE file (GPLv2 or later is the standard choice for WordPress-adjacent code) and stating it in your theme or plugin’s header comment avoids ambiguity for anyone who wants to fork, modify, or redistribute your code later, and keeps you aligned with how the broader WordPress ecosystem expects shared code to be licensed.

The Actual Answer

Track your custom code, exclude your secrets, keep media and the database out of Git entirely, run automated checks before anything deploys, and build a real deployment pipeline rather than manually syncing files. That gets you every real benefit GitHub offers, version history, collaboration, rollback, without the actual risk that “just upload the whole thing” carries. If you’re working solo on a small site with no team and no compliance requirements, the case for any of this is admittedly weaker, a solid backup plugin might cover your needs entirely. But the moment more than one person touches the codebase, or the site is complex enough that “what changed and when” is a question you need answered reliably, proper version control scoped correctly is worth the initial setup time, and it’s considerably cheaper to set up properly on day one than to untangle a bloated, secret-leaking repository six months into a project that grew past the point anyone expected. Get the boundary between code and everything else right at the start, and GitHub earns its place in a WordPress workflow rather than becoming another thing to clean up later.


Interesting Reads:

How To Make A Responsive Table In WordPress

How To Use SQLmap For WordPress

Is WordPress Canceled?

Reading
13 min · 2,572 words
Published
Sep 3, 2024
Shashank Dubey
BuddyX contributor

Writing about WordPress communities, BuddyPress, BuddyBoss, LMS plugins, and the business of paid communities.

Keep reading

More from the BuddyX blog

Browse all posts on community, WordPress, BuddyPress and the studio of plugins behind BuddyX.