Rather than feature branches, I'd recommend using feature flags. Essentially, these are just conditionals in your code that determine whether a particular portion of the code gets run or not. You control the status of the feature through configuration, which can be as simplistic as an app setting in your Web.config or as complex as a third-party service like LaunchDarkly, that gives you granular control over which users get access to which features at which time.
The one downside to feature flags is that they're inherently technical debt. Essentially, you have code that may not ever run in your codebase, and that code grows over time. Some companies actually embrace this, though. Facebook, for example, makes heavy use of feature flags and never clean them up. Personally, I think that's a little insane, but I suppose it works for them. For the rest of us, the common recommendation is that when a feature flag is introduced, you also add a PBI to clean it up. Then, once you have fully deployed this feature (and maybe given it some time to assure there's no major issues), you add the PBI to a sprint to get rid of the technical debt. It's not ideal, but it is manageable.
Technical debt aside, feature flags can give you enormous freedom. You can actually merge the code into master, but choose not to actually deploy the feature. That way, you don't have to worry about handling merges with code that's weeks or months out of step with master, but the actual product hasn't changed.
Better, you can simply patch to enable features, which is much less involved than full upgrades. For example, let's say you're following the typical monolithic release cycle. You've got a whole batch of new features, some of which require fundamental changes to the core product. The upgrade process has to touch a lot of files and make a lot of changes to enable all these new features. Then, there's a problem, a big problem, a problem that you can't just push a quick fix for. You've now got to handle downgrades, and figure out how your going to get your users back to a usable state. That's a beast of a problem.
Now, let's look at the feature flag approach. Since all the code for these features are behind flags. You simply do a release that turns them on. Of course, if they weren't actually in the last release at all, you'll also need to push down the code, which may require the same monolithic upgrade. However, where things get much better for you is when things explode. This time, you don't have to downgrade your users and plan a new monolithic release. Instead, you simply issue a patch that disables the feature(s) causing the issue. The application now reverts back to the way it did things before. When you fix the issue, you can roll forward, pushing only the fix and turning the feature back on, instead of having to re-release the whole product.
develop (in-development) -> release (in-staging) -> master (production-ready)
branches from git-flow can work just fine. It's specifically the long-running, always production-ready branch combined with development happening on other branches that would make life simple for you.