The Montclair State University Website: My Portfolio
In case you skipped the homepage of this website, my name is Matt Pierce and I'm the Senior Web Developer at Montclair State University. If you had gone to the university's website at any time in the past two decades, what you would've seen would've been running on my code.
On August 4, 2026 we finally launched the new website we'd been working on for the previous three years. The new site's code and architecture was completely outsourced to OHO Interactive, a marketing consulting agency, and so, when the new site launched, all of my code was retired.
I'm proud of the work I did on the Montclair website. And, since I'm effectively the only full-stack web developer who's ever worked on the University's website, I'm the only person who's ever seen any of it.
In the coming weeks and months I hope to be able to showcase some of my work here. I've never worked in any sort of structured development environment and I'm sure a lot of the things I post will run counter to industry best practices but I'm okay with that. With no training, consultants or education to speak of, we ran a fully-featured website on-premises with minimal investment. It was fast, it was light, and it was incredibly reliable, with almost all of our downtime coming from University-wide network outages, not problems with the site itself.
Server and Code Architecture
The University's website had been in (sort of) continuous operation since just before I started in 2007 - a Ship of Theseus continually being upgraded but never being replaced. The closest one could say we ever got to "replacing" the website is the two times we upgraded the server OS entirely - which is to say we set up new servers then copied over the site's files and databases on launch.
When I started we were running the website on Sun Solaris 8 on a dedicated UltraSPARC T1 (for both Apache and MySQL). When the site shut down it was running on a cluster of VMWare hosts - two application nodes and one database node - running RedHat Enterprise Linux 8. We moved from DreamWeaver augmented with custom web apps to TerminalFour to WordPress all on the same servers - no build-outs and change-overs.
That's not to say that we never ran into any trouble. Some time in 2008 or so we did get hacked. We'd used the JavaScript-based HTML editor Xinha as a part of our news system and back then Xinha was packaged with a plugin for uploading files that contained a remote code execution vulnerability. After that, we adopted a few principles that guided our management of the web server going forward.
- Don't trust third party plugins. We might not be virtuoso web developers, but for a lot of little tasks we found it best to write a small script that did one thing as safely as we could manage rather than to get a larger software suite that contained the thing we wanted to do. Ultimately we just felt more secure in our code because it wasn't a black box.
- Store code and temp files outside the web root whenever possible. By putting the code in a place that visitors can't reach, you help to ensure that even if an attacker gets a script into a temp directory, they can't load it directly. Also, keeping the bulk of your code outside of the web root helps to obfuscate it, should an attacker ever get in. We used the PHP
include_pathsetting to define an include directory, which also helped to keep our custom code cross-platform compatible. - The Apache user can only write to specific directories. This was enforced first with file permissions, then later through SE Linux policies. The idea here is that only specific known scripts should be triggering file writes, and we should know exactly what directories they're writing files into. And, even then, if someone finds an exploit in one of those scripts to upload a PHP file, that PHP file should not be able to do any damage to the actual web application.
- The MySQL user should only have permission to write under specific circumstances. While we never had a problem with an SQL injection attack, we extended our philosophy with Apache into MySQL. All of our custom web applications from that point forward used two separate MySQL accounts:
www_roandwww_rw. Thewww_roaccount was only ever grantedSELECTprivileges and we would use that account whenever we displayed data to an unauthenticated visitor. Usually we would only connect using thewww_rwaccount when we needed to make changes and even then that account could only useSELECT, INSERT, UPDATE, DELETE. No web application could connect to the MySQL server with an account that could actually alter the structure of the database. - Carefully plan updates and upgrades for zero downtime. Since we were always operating a Ship of Theseus server, we needed to plan our web application updates so that we wouldn't break things in the upgrade process. That meant never adding a new column to a MySQL table without specifying a sensible default value for all the existing entries. Never changing the type signature of a function except to add additional parameters with defaults - this way old code would continue to function while using the older calling convention. And last, it meant every code roll-out needed a game-plan - which files would be updated in what order, with a full understanding of how each file write would affect the application.
Some of these fell away when we migrated to WordPress - or rather we made exceptions for WordPress. For example, WordPress only allows for one MySQL username/password combination, so that user has to have read/write privileges (even if it's only connecting to the database to render a page for a visitor).
A fairly typical directory structure for one of our web applications would look a little like this:
Our site root
/opt/htdocs/www
University Calendar Front-end interface
/opt/htdocs/www/calendar
University Calendar Back-end interface
/opt/htdocs/www/calendar/admin
Our code directory
/opt/htdocs/php-includes/calendar
The front-end page /opt/htdocs/www/calendar/index.php would contain the directive require_once("calendar/module.php"); which would contain all the code that did actual work for the calendar. Of course the file /opt/htdocs/php-includes/calendar/module.php would in-turn require_once("./settings.php"); which would have all the server-specific settings for the module (database host/username/password).
If we had been using git, settings.php would've been in .gitignore but I never made that jump because IT didn't want to install git on the production web server, fearing it would "consume too much CPU." I just had to remember never to upload the settings file.
Assorted Site Components
Even though it all worked together to create a cohesive site, I tried to keep each part of the site as self-contained as I could, with planned interfaces (either in PHP or through a REST endpoint). For example, while most custom web applications had the need to authenticate users, the authentication was done through a module called, creatively enough, "LoginModule." The various applications could not actually read any data about the current user session directly, they could only call LoginModule's public functions: $LoginModule->Login($username, $password);, $LoginModule->Logout($bRememberMe); and $LoginModule->Validate(); which simply returns the current user, if the user is logged in.
I'll be building out this section over the next several months as I find time to spotlight different parts of the site.
- Utilities - This file contains a handful of convenience functions that show up in just about every web application I built. It's an excellent window into how I carefully planned changes to the codebase to prevent interruptions in service.